roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     }
1019     
1020 });
1021
1022
1023  
1024 /*
1025  * Based on:
1026  * Ext JS Library 1.1.1
1027  * Copyright(c) 2006-2007, Ext JS, LLC.
1028  *
1029  * Originally Released Under LGPL - original licence link has changed is not relivant.
1030  *
1031  * Fork - LGPL
1032  * <script type="text/javascript">
1033  */
1034
1035 /**
1036  * @class Date
1037  *
1038  * The date parsing and format syntax is a subset of
1039  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1040  * supported will provide results equivalent to their PHP versions.
1041  *
1042  * Following is the list of all currently supported formats:
1043  *<pre>
1044 Sample date:
1045 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1046
1047 Format  Output      Description
1048 ------  ----------  --------------------------------------------------------------
1049   d      10         Day of the month, 2 digits with leading zeros
1050   D      Wed        A textual representation of a day, three letters
1051   j      10         Day of the month without leading zeros
1052   l      Wednesday  A full textual representation of the day of the week
1053   S      th         English ordinal day of month suffix, 2 chars (use with j)
1054   w      3          Numeric representation of the day of the week
1055   z      9          The julian date, or day of the year (0-365)
1056   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1057   F      January    A full textual representation of the month
1058   m      01         Numeric representation of a month, with leading zeros
1059   M      Jan        Month name abbreviation, three letters
1060   n      1          Numeric representation of a month, without leading zeros
1061   t      31         Number of days in the given month
1062   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1063   Y      2007       A full numeric representation of a year, 4 digits
1064   y      07         A two digit representation of a year
1065   a      pm         Lowercase Ante meridiem and Post meridiem
1066   A      PM         Uppercase Ante meridiem and Post meridiem
1067   g      3          12-hour format of an hour without leading zeros
1068   G      15         24-hour format of an hour without leading zeros
1069   h      03         12-hour format of an hour with leading zeros
1070   H      15         24-hour format of an hour with leading zeros
1071   i      05         Minutes with leading zeros
1072   s      01         Seconds, with leading zeros
1073   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1074   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1075   T      CST        Timezone setting of the machine running the code
1076   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1077 </pre>
1078  *
1079  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1080  * <pre><code>
1081 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1082 document.write(dt.format('Y-m-d'));                         //2007-01-10
1083 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1084 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1085  </code></pre>
1086  *
1087  * Here are some standard date/time patterns that you might find helpful.  They
1088  * are not part of the source of Date.js, but to use them you can simply copy this
1089  * block of code into any script that is included after Date.js and they will also become
1090  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1091  * <pre><code>
1092 Date.patterns = {
1093     ISO8601Long:"Y-m-d H:i:s",
1094     ISO8601Short:"Y-m-d",
1095     ShortDate: "n/j/Y",
1096     LongDate: "l, F d, Y",
1097     FullDateTime: "l, F d, Y g:i:s A",
1098     MonthDay: "F d",
1099     ShortTime: "g:i A",
1100     LongTime: "g:i:s A",
1101     SortableDateTime: "Y-m-d\\TH:i:s",
1102     UniversalSortableDateTime: "Y-m-d H:i:sO",
1103     YearMonth: "F, Y"
1104 };
1105 </code></pre>
1106  *
1107  * Example usage:
1108  * <pre><code>
1109 var dt = new Date();
1110 document.write(dt.format(Date.patterns.ShortDate));
1111  </code></pre>
1112  */
1113
1114 /*
1115  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1116  * They generate precompiled functions from date formats instead of parsing and
1117  * processing the pattern every time you format a date.  These functions are available
1118  * on every Date object (any javascript function).
1119  *
1120  * The original article and download are here:
1121  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1122  *
1123  */
1124  
1125  
1126  // was in core
1127 /**
1128  Returns the number of milliseconds between this date and date
1129  @param {Date} date (optional) Defaults to now
1130  @return {Number} The diff in milliseconds
1131  @member Date getElapsed
1132  */
1133 Date.prototype.getElapsed = function(date) {
1134         return Math.abs((date || new Date()).getTime()-this.getTime());
1135 };
1136 // was in date file..
1137
1138
1139 // private
1140 Date.parseFunctions = {count:0};
1141 // private
1142 Date.parseRegexes = [];
1143 // private
1144 Date.formatFunctions = {count:0};
1145
1146 // private
1147 Date.prototype.dateFormat = function(format) {
1148     if (Date.formatFunctions[format] == null) {
1149         Date.createNewFormat(format);
1150     }
1151     var func = Date.formatFunctions[format];
1152     return this[func]();
1153 };
1154
1155
1156 /**
1157  * Formats a date given the supplied format string
1158  * @param {String} format The format string
1159  * @return {String} The formatted date
1160  * @method
1161  */
1162 Date.prototype.format = Date.prototype.dateFormat;
1163
1164 // private
1165 Date.createNewFormat = function(format) {
1166     var funcName = "format" + Date.formatFunctions.count++;
1167     Date.formatFunctions[format] = funcName;
1168     var code = "Date.prototype." + funcName + " = function(){return ";
1169     var special = false;
1170     var ch = '';
1171     for (var i = 0; i < format.length; ++i) {
1172         ch = format.charAt(i);
1173         if (!special && ch == "\\") {
1174             special = true;
1175         }
1176         else if (special) {
1177             special = false;
1178             code += "'" + String.escape(ch) + "' + ";
1179         }
1180         else {
1181             code += Date.getFormatCode(ch);
1182         }
1183     }
1184     /** eval:var:zzzzzzzzzzzzz */
1185     eval(code.substring(0, code.length - 3) + ";}");
1186 };
1187
1188 // private
1189 Date.getFormatCode = function(character) {
1190     switch (character) {
1191     case "d":
1192         return "String.leftPad(this.getDate(), 2, '0') + ";
1193     case "D":
1194         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1195     case "j":
1196         return "this.getDate() + ";
1197     case "l":
1198         return "Date.dayNames[this.getDay()] + ";
1199     case "S":
1200         return "this.getSuffix() + ";
1201     case "w":
1202         return "this.getDay() + ";
1203     case "z":
1204         return "this.getDayOfYear() + ";
1205     case "W":
1206         return "this.getWeekOfYear() + ";
1207     case "F":
1208         return "Date.monthNames[this.getMonth()] + ";
1209     case "m":
1210         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1211     case "M":
1212         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1213     case "n":
1214         return "(this.getMonth() + 1) + ";
1215     case "t":
1216         return "this.getDaysInMonth() + ";
1217     case "L":
1218         return "(this.isLeapYear() ? 1 : 0) + ";
1219     case "Y":
1220         return "this.getFullYear() + ";
1221     case "y":
1222         return "('' + this.getFullYear()).substring(2, 4) + ";
1223     case "a":
1224         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1225     case "A":
1226         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1227     case "g":
1228         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1229     case "G":
1230         return "this.getHours() + ";
1231     case "h":
1232         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1233     case "H":
1234         return "String.leftPad(this.getHours(), 2, '0') + ";
1235     case "i":
1236         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1237     case "s":
1238         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1239     case "O":
1240         return "this.getGMTOffset() + ";
1241     case "P":
1242         return "this.getGMTColonOffset() + ";
1243     case "T":
1244         return "this.getTimezone() + ";
1245     case "Z":
1246         return "(this.getTimezoneOffset() * -60) + ";
1247     default:
1248         return "'" + String.escape(character) + "' + ";
1249     }
1250 };
1251
1252 /**
1253  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1254  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1255  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1256  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1257  * string or the parse operation will fail.
1258  * Example Usage:
1259 <pre><code>
1260 //dt = Fri May 25 2007 (current date)
1261 var dt = new Date();
1262
1263 //dt = Thu May 25 2006 (today's month/day in 2006)
1264 dt = Date.parseDate("2006", "Y");
1265
1266 //dt = Sun Jan 15 2006 (all date parts specified)
1267 dt = Date.parseDate("2006-1-15", "Y-m-d");
1268
1269 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1270 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1271 </code></pre>
1272  * @param {String} input The unparsed date as a string
1273  * @param {String} format The format the date is in
1274  * @return {Date} The parsed date
1275  * @static
1276  */
1277 Date.parseDate = function(input, format) {
1278     if (Date.parseFunctions[format] == null) {
1279         Date.createParser(format);
1280     }
1281     var func = Date.parseFunctions[format];
1282     return Date[func](input);
1283 };
1284 /**
1285  * @private
1286  */
1287
1288 Date.createParser = function(format) {
1289     var funcName = "parse" + Date.parseFunctions.count++;
1290     var regexNum = Date.parseRegexes.length;
1291     var currentGroup = 1;
1292     Date.parseFunctions[format] = funcName;
1293
1294     var code = "Date." + funcName + " = function(input){\n"
1295         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1296         + "var d = new Date();\n"
1297         + "y = d.getFullYear();\n"
1298         + "m = d.getMonth();\n"
1299         + "d = d.getDate();\n"
1300         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1301         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1302         + "if (results && results.length > 0) {";
1303     var regex = "";
1304
1305     var special = false;
1306     var ch = '';
1307     for (var i = 0; i < format.length; ++i) {
1308         ch = format.charAt(i);
1309         if (!special && ch == "\\") {
1310             special = true;
1311         }
1312         else if (special) {
1313             special = false;
1314             regex += String.escape(ch);
1315         }
1316         else {
1317             var obj = Date.formatCodeToRegex(ch, currentGroup);
1318             currentGroup += obj.g;
1319             regex += obj.s;
1320             if (obj.g && obj.c) {
1321                 code += obj.c;
1322             }
1323         }
1324     }
1325
1326     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1327         + "{v = new Date(y, m, d, h, i, s);}\n"
1328         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1329         + "{v = new Date(y, m, d, h, i);}\n"
1330         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1331         + "{v = new Date(y, m, d, h);}\n"
1332         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1333         + "{v = new Date(y, m, d);}\n"
1334         + "else if (y >= 0 && m >= 0)\n"
1335         + "{v = new Date(y, m);}\n"
1336         + "else if (y >= 0)\n"
1337         + "{v = new Date(y);}\n"
1338         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1339         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1340         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1341         + ";}";
1342
1343     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1344     /** eval:var:zzzzzzzzzzzzz */
1345     eval(code);
1346 };
1347
1348 // private
1349 Date.formatCodeToRegex = function(character, currentGroup) {
1350     switch (character) {
1351     case "D":
1352         return {g:0,
1353         c:null,
1354         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1355     case "j":
1356         return {g:1,
1357             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1358             s:"(\\d{1,2})"}; // day of month without leading zeroes
1359     case "d":
1360         return {g:1,
1361             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1362             s:"(\\d{2})"}; // day of month with leading zeroes
1363     case "l":
1364         return {g:0,
1365             c:null,
1366             s:"(?:" + Date.dayNames.join("|") + ")"};
1367     case "S":
1368         return {g:0,
1369             c:null,
1370             s:"(?:st|nd|rd|th)"};
1371     case "w":
1372         return {g:0,
1373             c:null,
1374             s:"\\d"};
1375     case "z":
1376         return {g:0,
1377             c:null,
1378             s:"(?:\\d{1,3})"};
1379     case "W":
1380         return {g:0,
1381             c:null,
1382             s:"(?:\\d{2})"};
1383     case "F":
1384         return {g:1,
1385             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1386             s:"(" + Date.monthNames.join("|") + ")"};
1387     case "M":
1388         return {g:1,
1389             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1390             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1391     case "n":
1392         return {g:1,
1393             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1394             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1395     case "m":
1396         return {g:1,
1397             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1398             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1399     case "t":
1400         return {g:0,
1401             c:null,
1402             s:"\\d{1,2}"};
1403     case "L":
1404         return {g:0,
1405             c:null,
1406             s:"(?:1|0)"};
1407     case "Y":
1408         return {g:1,
1409             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1410             s:"(\\d{4})"};
1411     case "y":
1412         return {g:1,
1413             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1414                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1415             s:"(\\d{1,2})"};
1416     case "a":
1417         return {g:1,
1418             c:"if (results[" + currentGroup + "] == 'am') {\n"
1419                 + "if (h == 12) { h = 0; }\n"
1420                 + "} else { if (h < 12) { h += 12; }}",
1421             s:"(am|pm)"};
1422     case "A":
1423         return {g:1,
1424             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1425                 + "if (h == 12) { h = 0; }\n"
1426                 + "} else { if (h < 12) { h += 12; }}",
1427             s:"(AM|PM)"};
1428     case "g":
1429     case "G":
1430         return {g:1,
1431             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1432             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1433     case "h":
1434     case "H":
1435         return {g:1,
1436             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1437             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1438     case "i":
1439         return {g:1,
1440             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1441             s:"(\\d{2})"};
1442     case "s":
1443         return {g:1,
1444             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1445             s:"(\\d{2})"};
1446     case "O":
1447         return {g:1,
1448             c:[
1449                 "o = results[", currentGroup, "];\n",
1450                 "var sn = o.substring(0,1);\n", // get + / - sign
1451                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1452                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1453                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1454                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1455             ].join(""),
1456             s:"([+\-]\\d{2,4})"};
1457     
1458     
1459     case "P":
1460         return {g:1,
1461                 c:[
1462                    "o = results[", currentGroup, "];\n",
1463                    "var sn = o.substring(0,1);\n",
1464                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1465                    "var mn = o.substring(4,6) % 60;\n",
1466                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1467                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1468             ].join(""),
1469             s:"([+\-]\\d{4})"};
1470     case "T":
1471         return {g:0,
1472             c:null,
1473             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1474     case "Z":
1475         return {g:1,
1476             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1477                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1478             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1479     default:
1480         return {g:0,
1481             c:null,
1482             s:String.escape(character)};
1483     }
1484 };
1485
1486 /**
1487  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1488  * @return {String} The abbreviated timezone name (e.g. 'CST')
1489  */
1490 Date.prototype.getTimezone = function() {
1491     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1492 };
1493
1494 /**
1495  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1496  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1497  */
1498 Date.prototype.getGMTOffset = function() {
1499     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1500         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1501         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1502 };
1503
1504 /**
1505  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1506  * @return {String} 2-characters representing hours and 2-characters representing minutes
1507  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1508  */
1509 Date.prototype.getGMTColonOffset = function() {
1510         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1511                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1512                 + ":"
1513                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1514 }
1515
1516 /**
1517  * Get the numeric day number of the year, adjusted for leap year.
1518  * @return {Number} 0 through 364 (365 in leap years)
1519  */
1520 Date.prototype.getDayOfYear = function() {
1521     var num = 0;
1522     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1523     for (var i = 0; i < this.getMonth(); ++i) {
1524         num += Date.daysInMonth[i];
1525     }
1526     return num + this.getDate() - 1;
1527 };
1528
1529 /**
1530  * Get the string representation of the numeric week number of the year
1531  * (equivalent to the format specifier 'W').
1532  * @return {String} '00' through '52'
1533  */
1534 Date.prototype.getWeekOfYear = function() {
1535     // Skip to Thursday of this week
1536     var now = this.getDayOfYear() + (4 - this.getDay());
1537     // Find the first Thursday of the year
1538     var jan1 = new Date(this.getFullYear(), 0, 1);
1539     var then = (7 - jan1.getDay() + 4);
1540     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1541 };
1542
1543 /**
1544  * Whether or not the current date is in a leap year.
1545  * @return {Boolean} True if the current date is in a leap year, else false
1546  */
1547 Date.prototype.isLeapYear = function() {
1548     var year = this.getFullYear();
1549     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1550 };
1551
1552 /**
1553  * Get the first day of the current month, adjusted for leap year.  The returned value
1554  * is the numeric day index within the week (0-6) which can be used in conjunction with
1555  * the {@link #monthNames} array to retrieve the textual day name.
1556  * Example:
1557  *<pre><code>
1558 var dt = new Date('1/10/2007');
1559 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1560 </code></pre>
1561  * @return {Number} The day number (0-6)
1562  */
1563 Date.prototype.getFirstDayOfMonth = function() {
1564     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1565     return (day < 0) ? (day + 7) : day;
1566 };
1567
1568 /**
1569  * Get the last day of the current month, adjusted for leap year.  The returned value
1570  * is the numeric day index within the week (0-6) which can be used in conjunction with
1571  * the {@link #monthNames} array to retrieve the textual day name.
1572  * Example:
1573  *<pre><code>
1574 var dt = new Date('1/10/2007');
1575 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1576 </code></pre>
1577  * @return {Number} The day number (0-6)
1578  */
1579 Date.prototype.getLastDayOfMonth = function() {
1580     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1581     return (day < 0) ? (day + 7) : day;
1582 };
1583
1584
1585 /**
1586  * Get the first date of this date's month
1587  * @return {Date}
1588  */
1589 Date.prototype.getFirstDateOfMonth = function() {
1590     return new Date(this.getFullYear(), this.getMonth(), 1);
1591 };
1592
1593 /**
1594  * Get the last date of this date's month
1595  * @return {Date}
1596  */
1597 Date.prototype.getLastDateOfMonth = function() {
1598     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1599 };
1600 /**
1601  * Get the number of days in the current month, adjusted for leap year.
1602  * @return {Number} The number of days in the month
1603  */
1604 Date.prototype.getDaysInMonth = function() {
1605     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1606     return Date.daysInMonth[this.getMonth()];
1607 };
1608
1609 /**
1610  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1611  * @return {String} 'st, 'nd', 'rd' or 'th'
1612  */
1613 Date.prototype.getSuffix = function() {
1614     switch (this.getDate()) {
1615         case 1:
1616         case 21:
1617         case 31:
1618             return "st";
1619         case 2:
1620         case 22:
1621             return "nd";
1622         case 3:
1623         case 23:
1624             return "rd";
1625         default:
1626             return "th";
1627     }
1628 };
1629
1630 // private
1631 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1632
1633 /**
1634  * An array of textual month names.
1635  * Override these values for international dates, for example...
1636  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1637  * @type Array
1638  * @static
1639  */
1640 Date.monthNames =
1641    ["January",
1642     "February",
1643     "March",
1644     "April",
1645     "May",
1646     "June",
1647     "July",
1648     "August",
1649     "September",
1650     "October",
1651     "November",
1652     "December"];
1653
1654 /**
1655  * An array of textual day names.
1656  * Override these values for international dates, for example...
1657  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1658  * @type Array
1659  * @static
1660  */
1661 Date.dayNames =
1662    ["Sunday",
1663     "Monday",
1664     "Tuesday",
1665     "Wednesday",
1666     "Thursday",
1667     "Friday",
1668     "Saturday"];
1669
1670 // private
1671 Date.y2kYear = 50;
1672 // private
1673 Date.monthNumbers = {
1674     Jan:0,
1675     Feb:1,
1676     Mar:2,
1677     Apr:3,
1678     May:4,
1679     Jun:5,
1680     Jul:6,
1681     Aug:7,
1682     Sep:8,
1683     Oct:9,
1684     Nov:10,
1685     Dec:11};
1686
1687 /**
1688  * Creates and returns a new Date instance with the exact same date value as the called instance.
1689  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1690  * variable will also be changed.  When the intention is to create a new variable that will not
1691  * modify the original instance, you should create a clone.
1692  *
1693  * Example of correctly cloning a date:
1694  * <pre><code>
1695 //wrong way:
1696 var orig = new Date('10/1/2006');
1697 var copy = orig;
1698 copy.setDate(5);
1699 document.write(orig);  //returns 'Thu Oct 05 2006'!
1700
1701 //correct way:
1702 var orig = new Date('10/1/2006');
1703 var copy = orig.clone();
1704 copy.setDate(5);
1705 document.write(orig);  //returns 'Thu Oct 01 2006'
1706 </code></pre>
1707  * @return {Date} The new Date instance
1708  */
1709 Date.prototype.clone = function() {
1710         return new Date(this.getTime());
1711 };
1712
1713 /**
1714  * Clears any time information from this date
1715  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1716  @return {Date} this or the clone
1717  */
1718 Date.prototype.clearTime = function(clone){
1719     if(clone){
1720         return this.clone().clearTime();
1721     }
1722     this.setHours(0);
1723     this.setMinutes(0);
1724     this.setSeconds(0);
1725     this.setMilliseconds(0);
1726     return this;
1727 };
1728
1729 // private
1730 // safari setMonth is broken -- check that this is only donw once...
1731 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1732     Date.brokenSetMonth = Date.prototype.setMonth;
1733         Date.prototype.setMonth = function(num){
1734                 if(num <= -1){
1735                         var n = Math.ceil(-num);
1736                         var back_year = Math.ceil(n/12);
1737                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1738                         this.setFullYear(this.getFullYear() - back_year);
1739                         return Date.brokenSetMonth.call(this, month);
1740                 } else {
1741                         return Date.brokenSetMonth.apply(this, arguments);
1742                 }
1743         };
1744 }
1745
1746 /** Date interval constant 
1747 * @static 
1748 * @type String */
1749 Date.MILLI = "ms";
1750 /** Date interval constant 
1751 * @static 
1752 * @type String */
1753 Date.SECOND = "s";
1754 /** Date interval constant 
1755 * @static 
1756 * @type String */
1757 Date.MINUTE = "mi";
1758 /** Date interval constant 
1759 * @static 
1760 * @type String */
1761 Date.HOUR = "h";
1762 /** Date interval constant 
1763 * @static 
1764 * @type String */
1765 Date.DAY = "d";
1766 /** Date interval constant 
1767 * @static 
1768 * @type String */
1769 Date.MONTH = "mo";
1770 /** Date interval constant 
1771 * @static 
1772 * @type String */
1773 Date.YEAR = "y";
1774
1775 /**
1776  * Provides a convenient method of performing basic date arithmetic.  This method
1777  * does not modify the Date instance being called - it creates and returns
1778  * a new Date instance containing the resulting date value.
1779  *
1780  * Examples:
1781  * <pre><code>
1782 //Basic usage:
1783 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1784 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1785
1786 //Negative values will subtract correctly:
1787 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1788 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1789
1790 //You can even chain several calls together in one line!
1791 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1792 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1793  </code></pre>
1794  *
1795  * @param {String} interval   A valid date interval enum value
1796  * @param {Number} value      The amount to add to the current date
1797  * @return {Date} The new Date instance
1798  */
1799 Date.prototype.add = function(interval, value){
1800   var d = this.clone();
1801   if (!interval || value === 0) { return d; }
1802   switch(interval.toLowerCase()){
1803     case Date.MILLI:
1804       d.setMilliseconds(this.getMilliseconds() + value);
1805       break;
1806     case Date.SECOND:
1807       d.setSeconds(this.getSeconds() + value);
1808       break;
1809     case Date.MINUTE:
1810       d.setMinutes(this.getMinutes() + value);
1811       break;
1812     case Date.HOUR:
1813       d.setHours(this.getHours() + value);
1814       break;
1815     case Date.DAY:
1816       d.setDate(this.getDate() + value);
1817       break;
1818     case Date.MONTH:
1819       var day = this.getDate();
1820       if(day > 28){
1821           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1822       }
1823       d.setDate(day);
1824       d.setMonth(this.getMonth() + value);
1825       break;
1826     case Date.YEAR:
1827       d.setFullYear(this.getFullYear() + value);
1828       break;
1829   }
1830   return d;
1831 };
1832 /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842
1843 /**
1844  * @class Roo.lib.Dom
1845  * @static
1846  * 
1847  * Dom utils (from YIU afaik)
1848  * 
1849  **/
1850 Roo.lib.Dom = {
1851     /**
1852      * Get the view width
1853      * @param {Boolean} full True will get the full document, otherwise it's the view width
1854      * @return {Number} The width
1855      */
1856      
1857     getViewWidth : function(full) {
1858         return full ? this.getDocumentWidth() : this.getViewportWidth();
1859     },
1860     /**
1861      * Get the view height
1862      * @param {Boolean} full True will get the full document, otherwise it's the view height
1863      * @return {Number} The height
1864      */
1865     getViewHeight : function(full) {
1866         return full ? this.getDocumentHeight() : this.getViewportHeight();
1867     },
1868
1869     getDocumentHeight: function() {
1870         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1871         return Math.max(scrollHeight, this.getViewportHeight());
1872     },
1873
1874     getDocumentWidth: function() {
1875         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1876         return Math.max(scrollWidth, this.getViewportWidth());
1877     },
1878
1879     getViewportHeight: function() {
1880         var height = self.innerHeight;
1881         var mode = document.compatMode;
1882
1883         if ((mode || Roo.isIE) && !Roo.isOpera) {
1884             height = (mode == "CSS1Compat") ?
1885                      document.documentElement.clientHeight :
1886                      document.body.clientHeight;
1887         }
1888
1889         return height;
1890     },
1891
1892     getViewportWidth: function() {
1893         var width = self.innerWidth;
1894         var mode = document.compatMode;
1895
1896         if (mode || Roo.isIE) {
1897             width = (mode == "CSS1Compat") ?
1898                     document.documentElement.clientWidth :
1899                     document.body.clientWidth;
1900         }
1901         return width;
1902     },
1903
1904     isAncestor : function(p, c) {
1905         p = Roo.getDom(p);
1906         c = Roo.getDom(c);
1907         if (!p || !c) {
1908             return false;
1909         }
1910
1911         if (p.contains && !Roo.isSafari) {
1912             return p.contains(c);
1913         } else if (p.compareDocumentPosition) {
1914             return !!(p.compareDocumentPosition(c) & 16);
1915         } else {
1916             var parent = c.parentNode;
1917             while (parent) {
1918                 if (parent == p) {
1919                     return true;
1920                 }
1921                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1922                     return false;
1923                 }
1924                 parent = parent.parentNode;
1925             }
1926             return false;
1927         }
1928     },
1929
1930     getRegion : function(el) {
1931         return Roo.lib.Region.getRegion(el);
1932     },
1933
1934     getY : function(el) {
1935         return this.getXY(el)[1];
1936     },
1937
1938     getX : function(el) {
1939         return this.getXY(el)[0];
1940     },
1941
1942     getXY : function(el) {
1943         var p, pe, b, scroll, bd = document.body;
1944         el = Roo.getDom(el);
1945         var fly = Roo.lib.AnimBase.fly;
1946         if (el.getBoundingClientRect) {
1947             b = el.getBoundingClientRect();
1948             scroll = fly(document).getScroll();
1949             return [b.left + scroll.left, b.top + scroll.top];
1950         }
1951         var x = 0, y = 0;
1952
1953         p = el;
1954
1955         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1956
1957         while (p) {
1958
1959             x += p.offsetLeft;
1960             y += p.offsetTop;
1961
1962             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1963                 hasAbsolute = true;
1964             }
1965
1966             if (Roo.isGecko) {
1967                 pe = fly(p);
1968
1969                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1970                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1971
1972
1973                 x += bl;
1974                 y += bt;
1975
1976
1977                 if (p != el && pe.getStyle('overflow') != 'visible') {
1978                     x += bl;
1979                     y += bt;
1980                 }
1981             }
1982             p = p.offsetParent;
1983         }
1984
1985         if (Roo.isSafari && hasAbsolute) {
1986             x -= bd.offsetLeft;
1987             y -= bd.offsetTop;
1988         }
1989
1990         if (Roo.isGecko && !hasAbsolute) {
1991             var dbd = fly(bd);
1992             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1993             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1994         }
1995
1996         p = el.parentNode;
1997         while (p && p != bd) {
1998             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1999                 x -= p.scrollLeft;
2000                 y -= p.scrollTop;
2001             }
2002             p = p.parentNode;
2003         }
2004         return [x, y];
2005     },
2006  
2007   
2008
2009
2010     setXY : function(el, xy) {
2011         el = Roo.fly(el, '_setXY');
2012         el.position();
2013         var pts = el.translatePoints(xy);
2014         if (xy[0] !== false) {
2015             el.dom.style.left = pts.left + "px";
2016         }
2017         if (xy[1] !== false) {
2018             el.dom.style.top = pts.top + "px";
2019         }
2020     },
2021
2022     setX : function(el, x) {
2023         this.setXY(el, [x, false]);
2024     },
2025
2026     setY : function(el, y) {
2027         this.setXY(el, [false, y]);
2028     }
2029 };
2030 /*
2031  * Portions of this file are based on pieces of Yahoo User Interface Library
2032  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2033  * YUI licensed under the BSD License:
2034  * http://developer.yahoo.net/yui/license.txt
2035  * <script type="text/javascript">
2036  *
2037  */
2038
2039 Roo.lib.Event = function() {
2040     var loadComplete = false;
2041     var listeners = [];
2042     var unloadListeners = [];
2043     var retryCount = 0;
2044     var onAvailStack = [];
2045     var counter = 0;
2046     var lastError = null;
2047
2048     return {
2049         POLL_RETRYS: 200,
2050         POLL_INTERVAL: 20,
2051         EL: 0,
2052         TYPE: 1,
2053         FN: 2,
2054         WFN: 3,
2055         OBJ: 3,
2056         ADJ_SCOPE: 4,
2057         _interval: null,
2058
2059         startInterval: function() {
2060             if (!this._interval) {
2061                 var self = this;
2062                 var callback = function() {
2063                     self._tryPreloadAttach();
2064                 };
2065                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2066
2067             }
2068         },
2069
2070         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2071             onAvailStack.push({ id:         p_id,
2072                 fn:         p_fn,
2073                 obj:        p_obj,
2074                 override:   p_override,
2075                 checkReady: false    });
2076
2077             retryCount = this.POLL_RETRYS;
2078             this.startInterval();
2079         },
2080
2081
2082         addListener: function(el, eventName, fn) {
2083             el = Roo.getDom(el);
2084             if (!el || !fn) {
2085                 return false;
2086             }
2087
2088             if ("unload" == eventName) {
2089                 unloadListeners[unloadListeners.length] =
2090                 [el, eventName, fn];
2091                 return true;
2092             }
2093
2094             var wrappedFn = function(e) {
2095                 return fn(Roo.lib.Event.getEvent(e));
2096             };
2097
2098             var li = [el, eventName, fn, wrappedFn];
2099
2100             var index = listeners.length;
2101             listeners[index] = li;
2102
2103             this.doAdd(el, eventName, wrappedFn, false);
2104             return true;
2105
2106         },
2107
2108
2109         removeListener: function(el, eventName, fn) {
2110             var i, len;
2111
2112             el = Roo.getDom(el);
2113
2114             if(!fn) {
2115                 return this.purgeElement(el, false, eventName);
2116             }
2117
2118
2119             if ("unload" == eventName) {
2120
2121                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2122                     var li = unloadListeners[i];
2123                     if (li &&
2124                         li[0] == el &&
2125                         li[1] == eventName &&
2126                         li[2] == fn) {
2127                         unloadListeners.splice(i, 1);
2128                         return true;
2129                     }
2130                 }
2131
2132                 return false;
2133             }
2134
2135             var cacheItem = null;
2136
2137
2138             var index = arguments[3];
2139
2140             if ("undefined" == typeof index) {
2141                 index = this._getCacheIndex(el, eventName, fn);
2142             }
2143
2144             if (index >= 0) {
2145                 cacheItem = listeners[index];
2146             }
2147
2148             if (!el || !cacheItem) {
2149                 return false;
2150             }
2151
2152             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2153
2154             delete listeners[index][this.WFN];
2155             delete listeners[index][this.FN];
2156             listeners.splice(index, 1);
2157
2158             return true;
2159
2160         },
2161
2162
2163         getTarget: function(ev, resolveTextNode) {
2164             ev = ev.browserEvent || ev;
2165             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2166             var t = ev.target || ev.srcElement;
2167             return this.resolveTextNode(t);
2168         },
2169
2170
2171         resolveTextNode: function(node) {
2172             if (Roo.isSafari && node && 3 == node.nodeType) {
2173                 return node.parentNode;
2174             } else {
2175                 return node;
2176             }
2177         },
2178
2179
2180         getPageX: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2183             var x = ev.pageX;
2184             if (!x && 0 !== x) {
2185                 x = ev.clientX || 0;
2186
2187                 if (Roo.isIE) {
2188                     x += this.getScroll()[1];
2189                 }
2190             }
2191
2192             return x;
2193         },
2194
2195
2196         getPageY: function(ev) {
2197             ev = ev.browserEvent || ev;
2198             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2199             var y = ev.pageY;
2200             if (!y && 0 !== y) {
2201                 y = ev.clientY || 0;
2202
2203                 if (Roo.isIE) {
2204                     y += this.getScroll()[0];
2205                 }
2206             }
2207
2208
2209             return y;
2210         },
2211
2212
2213         getXY: function(ev) {
2214             ev = ev.browserEvent || ev;
2215             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2216             return [this.getPageX(ev), this.getPageY(ev)];
2217         },
2218
2219
2220         getRelatedTarget: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2223             var t = ev.relatedTarget;
2224             if (!t) {
2225                 if (ev.type == "mouseout") {
2226                     t = ev.toElement;
2227                 } else if (ev.type == "mouseover") {
2228                     t = ev.fromElement;
2229                 }
2230             }
2231
2232             return this.resolveTextNode(t);
2233         },
2234
2235
2236         getTime: function(ev) {
2237             ev = ev.browserEvent || ev;
2238             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2239             if (!ev.time) {
2240                 var t = new Date().getTime();
2241                 try {
2242                     ev.time = t;
2243                 } catch(ex) {
2244                     this.lastError = ex;
2245                     return t;
2246                 }
2247             }
2248
2249             return ev.time;
2250         },
2251
2252
2253         stopEvent: function(ev) {
2254             this.stopPropagation(ev);
2255             this.preventDefault(ev);
2256         },
2257
2258
2259         stopPropagation: function(ev) {
2260             ev = ev.browserEvent || ev;
2261             if (ev.stopPropagation) {
2262                 ev.stopPropagation();
2263             } else {
2264                 ev.cancelBubble = true;
2265             }
2266         },
2267
2268
2269         preventDefault: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             if(ev.preventDefault) {
2272                 ev.preventDefault();
2273             } else {
2274                 ev.returnValue = false;
2275             }
2276         },
2277
2278
2279         getEvent: function(e) {
2280             var ev = e || window.event;
2281             if (!ev) {
2282                 var c = this.getEvent.caller;
2283                 while (c) {
2284                     ev = c.arguments[0];
2285                     if (ev && Event == ev.constructor) {
2286                         break;
2287                     }
2288                     c = c.caller;
2289                 }
2290             }
2291             return ev;
2292         },
2293
2294
2295         getCharCode: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             return ev.charCode || ev.keyCode || 0;
2298         },
2299
2300
2301         _getCacheIndex: function(el, eventName, fn) {
2302             for (var i = 0,len = listeners.length; i < len; ++i) {
2303                 var li = listeners[i];
2304                 if (li &&
2305                     li[this.FN] == fn &&
2306                     li[this.EL] == el &&
2307                     li[this.TYPE] == eventName) {
2308                     return i;
2309                 }
2310             }
2311
2312             return -1;
2313         },
2314
2315
2316         elCache: {},
2317
2318
2319         getEl: function(id) {
2320             return document.getElementById(id);
2321         },
2322
2323
2324         clearCache: function() {
2325         },
2326
2327
2328         _load: function(e) {
2329             loadComplete = true;
2330             var EU = Roo.lib.Event;
2331
2332
2333             if (Roo.isIE) {
2334                 EU.doRemove(window, "load", EU._load);
2335             }
2336         },
2337
2338
2339         _tryPreloadAttach: function() {
2340
2341             if (this.locked) {
2342                 return false;
2343             }
2344
2345             this.locked = true;
2346
2347
2348             var tryAgain = !loadComplete;
2349             if (!tryAgain) {
2350                 tryAgain = (retryCount > 0);
2351             }
2352
2353
2354             var notAvail = [];
2355             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2356                 var item = onAvailStack[i];
2357                 if (item) {
2358                     var el = this.getEl(item.id);
2359
2360                     if (el) {
2361                         if (!item.checkReady ||
2362                             loadComplete ||
2363                             el.nextSibling ||
2364                             (document && document.body)) {
2365
2366                             var scope = el;
2367                             if (item.override) {
2368                                 if (item.override === true) {
2369                                     scope = item.obj;
2370                                 } else {
2371                                     scope = item.override;
2372                                 }
2373                             }
2374                             item.fn.call(scope, item.obj);
2375                             onAvailStack[i] = null;
2376                         }
2377                     } else {
2378                         notAvail.push(item);
2379                     }
2380                 }
2381             }
2382
2383             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2384
2385             if (tryAgain) {
2386
2387                 this.startInterval();
2388             } else {
2389                 clearInterval(this._interval);
2390                 this._interval = null;
2391             }
2392
2393             this.locked = false;
2394
2395             return true;
2396
2397         },
2398
2399
2400         purgeElement: function(el, recurse, eventName) {
2401             var elListeners = this.getListeners(el, eventName);
2402             if (elListeners) {
2403                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2404                     var l = elListeners[i];
2405                     this.removeListener(el, l.type, l.fn);
2406                 }
2407             }
2408
2409             if (recurse && el && el.childNodes) {
2410                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2411                     this.purgeElement(el.childNodes[i], recurse, eventName);
2412                 }
2413             }
2414         },
2415
2416
2417         getListeners: function(el, eventName) {
2418             var results = [], searchLists;
2419             if (!eventName) {
2420                 searchLists = [listeners, unloadListeners];
2421             } else if (eventName == "unload") {
2422                 searchLists = [unloadListeners];
2423             } else {
2424                 searchLists = [listeners];
2425             }
2426
2427             for (var j = 0; j < searchLists.length; ++j) {
2428                 var searchList = searchLists[j];
2429                 if (searchList && searchList.length > 0) {
2430                     for (var i = 0,len = searchList.length; i < len; ++i) {
2431                         var l = searchList[i];
2432                         if (l && l[this.EL] === el &&
2433                             (!eventName || eventName === l[this.TYPE])) {
2434                             results.push({
2435                                 type:   l[this.TYPE],
2436                                 fn:     l[this.FN],
2437                                 obj:    l[this.OBJ],
2438                                 adjust: l[this.ADJ_SCOPE],
2439                                 index:  i
2440                             });
2441                         }
2442                     }
2443                 }
2444             }
2445
2446             return (results.length) ? results : null;
2447         },
2448
2449
2450         _unload: function(e) {
2451
2452             var EU = Roo.lib.Event, i, j, l, len, index;
2453
2454             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2455                 l = unloadListeners[i];
2456                 if (l) {
2457                     var scope = window;
2458                     if (l[EU.ADJ_SCOPE]) {
2459                         if (l[EU.ADJ_SCOPE] === true) {
2460                             scope = l[EU.OBJ];
2461                         } else {
2462                             scope = l[EU.ADJ_SCOPE];
2463                         }
2464                     }
2465                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2466                     unloadListeners[i] = null;
2467                     l = null;
2468                     scope = null;
2469                 }
2470             }
2471
2472             unloadListeners = null;
2473
2474             if (listeners && listeners.length > 0) {
2475                 j = listeners.length;
2476                 while (j) {
2477                     index = j - 1;
2478                     l = listeners[index];
2479                     if (l) {
2480                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2481                                 l[EU.FN], index);
2482                     }
2483                     j = j - 1;
2484                 }
2485                 l = null;
2486
2487                 EU.clearCache();
2488             }
2489
2490             EU.doRemove(window, "unload", EU._unload);
2491
2492         },
2493
2494
2495         getScroll: function() {
2496             var dd = document.documentElement, db = document.body;
2497             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2498                 return [dd.scrollTop, dd.scrollLeft];
2499             } else if (db) {
2500                 return [db.scrollTop, db.scrollLeft];
2501             } else {
2502                 return [0, 0];
2503             }
2504         },
2505
2506
2507         doAdd: function () {
2508             if (window.addEventListener) {
2509                 return function(el, eventName, fn, capture) {
2510                     el.addEventListener(eventName, fn, (capture));
2511                 };
2512             } else if (window.attachEvent) {
2513                 return function(el, eventName, fn, capture) {
2514                     el.attachEvent("on" + eventName, fn);
2515                 };
2516             } else {
2517                 return function() {
2518                 };
2519             }
2520         }(),
2521
2522
2523         doRemove: function() {
2524             if (window.removeEventListener) {
2525                 return function (el, eventName, fn, capture) {
2526                     el.removeEventListener(eventName, fn, (capture));
2527                 };
2528             } else if (window.detachEvent) {
2529                 return function (el, eventName, fn) {
2530                     el.detachEvent("on" + eventName, fn);
2531                 };
2532             } else {
2533                 return function() {
2534                 };
2535             }
2536         }()
2537     };
2538     
2539 }();
2540 (function() {     
2541    
2542     var E = Roo.lib.Event;
2543     E.on = E.addListener;
2544     E.un = E.removeListener;
2545
2546     if (document && document.body) {
2547         E._load();
2548     } else {
2549         E.doAdd(window, "load", E._load);
2550     }
2551     E.doAdd(window, "unload", E._unload);
2552     E._tryPreloadAttach();
2553 })();
2554
2555 /*
2556  * Portions of this file are based on pieces of Yahoo User Interface Library
2557  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2558  * YUI licensed under the BSD License:
2559  * http://developer.yahoo.net/yui/license.txt
2560  * <script type="text/javascript">
2561  *
2562  */
2563
2564 (function() {
2565     /**
2566      * @class Roo.lib.Ajax
2567      *
2568      */
2569     Roo.lib.Ajax = {
2570         /**
2571          * @static 
2572          */
2573         request : function(method, uri, cb, data, options) {
2574             if(options){
2575                 var hs = options.headers;
2576                 if(hs){
2577                     for(var h in hs){
2578                         if(hs.hasOwnProperty(h)){
2579                             this.initHeader(h, hs[h], false);
2580                         }
2581                     }
2582                 }
2583                 if(options.xmlData){
2584                     this.initHeader('Content-Type', 'text/xml', false);
2585                     method = 'POST';
2586                     data = options.xmlData;
2587                 }
2588             }
2589
2590             return this.asyncRequest(method, uri, cb, data);
2591         },
2592
2593         serializeForm : function(form) {
2594             if(typeof form == 'string') {
2595                 form = (document.getElementById(form) || document.forms[form]);
2596             }
2597
2598             var el, name, val, disabled, data = '', hasSubmit = false;
2599             for (var i = 0; i < form.elements.length; i++) {
2600                 el = form.elements[i];
2601                 disabled = form.elements[i].disabled;
2602                 name = form.elements[i].name;
2603                 val = form.elements[i].value;
2604
2605                 if (!disabled && name){
2606                     switch (el.type)
2607                             {
2608                         case 'select-one':
2609                         case 'select-multiple':
2610                             for (var j = 0; j < el.options.length; j++) {
2611                                 if (el.options[j].selected) {
2612                                     if (Roo.isIE) {
2613                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2614                                     }
2615                                     else {
2616                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2617                                     }
2618                                 }
2619                             }
2620                             break;
2621                         case 'radio':
2622                         case 'checkbox':
2623                             if (el.checked) {
2624                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2625                             }
2626                             break;
2627                         case 'file':
2628
2629                         case undefined:
2630
2631                         case 'reset':
2632
2633                         case 'button':
2634
2635                             break;
2636                         case 'submit':
2637                             if(hasSubmit == false) {
2638                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2639                                 hasSubmit = true;
2640                             }
2641                             break;
2642                         default:
2643                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2644                             break;
2645                     }
2646                 }
2647             }
2648             data = data.substr(0, data.length - 1);
2649             return data;
2650         },
2651
2652         headers:{},
2653
2654         hasHeaders:false,
2655
2656         useDefaultHeader:true,
2657
2658         defaultPostHeader:'application/x-www-form-urlencoded',
2659
2660         useDefaultXhrHeader:true,
2661
2662         defaultXhrHeader:'XMLHttpRequest',
2663
2664         hasDefaultHeaders:true,
2665
2666         defaultHeaders:{},
2667
2668         poll:{},
2669
2670         timeout:{},
2671
2672         pollInterval:50,
2673
2674         transactionId:0,
2675
2676         setProgId:function(id)
2677         {
2678             this.activeX.unshift(id);
2679         },
2680
2681         setDefaultPostHeader:function(b)
2682         {
2683             this.useDefaultHeader = b;
2684         },
2685
2686         setDefaultXhrHeader:function(b)
2687         {
2688             this.useDefaultXhrHeader = b;
2689         },
2690
2691         setPollingInterval:function(i)
2692         {
2693             if (typeof i == 'number' && isFinite(i)) {
2694                 this.pollInterval = i;
2695             }
2696         },
2697
2698         createXhrObject:function(transactionId)
2699         {
2700             var obj,http;
2701             try
2702             {
2703
2704                 http = new XMLHttpRequest();
2705
2706                 obj = { conn:http, tId:transactionId };
2707             }
2708             catch(e)
2709             {
2710                 for (var i = 0; i < this.activeX.length; ++i) {
2711                     try
2712                     {
2713
2714                         http = new ActiveXObject(this.activeX[i]);
2715
2716                         obj = { conn:http, tId:transactionId };
2717                         break;
2718                     }
2719                     catch(e) {
2720                     }
2721                 }
2722             }
2723             finally
2724             {
2725                 return obj;
2726             }
2727         },
2728
2729         getConnectionObject:function()
2730         {
2731             var o;
2732             var tId = this.transactionId;
2733
2734             try
2735             {
2736                 o = this.createXhrObject(tId);
2737                 if (o) {
2738                     this.transactionId++;
2739                 }
2740             }
2741             catch(e) {
2742             }
2743             finally
2744             {
2745                 return o;
2746             }
2747         },
2748
2749         asyncRequest:function(method, uri, callback, postData)
2750         {
2751             var o = this.getConnectionObject();
2752
2753             if (!o) {
2754                 return null;
2755             }
2756             else {
2757                 o.conn.open(method, uri, true);
2758
2759                 if (this.useDefaultXhrHeader) {
2760                     if (!this.defaultHeaders['X-Requested-With']) {
2761                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2762                     }
2763                 }
2764
2765                 if(postData && this.useDefaultHeader){
2766                     this.initHeader('Content-Type', this.defaultPostHeader);
2767                 }
2768
2769                  if (this.hasDefaultHeaders || this.hasHeaders) {
2770                     this.setHeader(o);
2771                 }
2772
2773                 this.handleReadyState(o, callback);
2774                 o.conn.send(postData || null);
2775
2776                 return o;
2777             }
2778         },
2779
2780         handleReadyState:function(o, callback)
2781         {
2782             var oConn = this;
2783
2784             if (callback && callback.timeout) {
2785                 
2786                 this.timeout[o.tId] = window.setTimeout(function() {
2787                     oConn.abort(o, callback, true);
2788                 }, callback.timeout);
2789             }
2790
2791             this.poll[o.tId] = window.setInterval(
2792                     function() {
2793                         if (o.conn && o.conn.readyState == 4) {
2794                             window.clearInterval(oConn.poll[o.tId]);
2795                             delete oConn.poll[o.tId];
2796
2797                             if(callback && callback.timeout) {
2798                                 window.clearTimeout(oConn.timeout[o.tId]);
2799                                 delete oConn.timeout[o.tId];
2800                             }
2801
2802                             oConn.handleTransactionResponse(o, callback);
2803                         }
2804                     }
2805                     , this.pollInterval);
2806         },
2807
2808         handleTransactionResponse:function(o, callback, isAbort)
2809         {
2810
2811             if (!callback) {
2812                 this.releaseObject(o);
2813                 return;
2814             }
2815
2816             var httpStatus, responseObject;
2817
2818             try
2819             {
2820                 if (o.conn.status !== undefined && o.conn.status != 0) {
2821                     httpStatus = o.conn.status;
2822                 }
2823                 else {
2824                     httpStatus = 13030;
2825                 }
2826             }
2827             catch(e) {
2828
2829
2830                 httpStatus = 13030;
2831             }
2832
2833             if (httpStatus >= 200 && httpStatus < 300) {
2834                 responseObject = this.createResponseObject(o, callback.argument);
2835                 if (callback.success) {
2836                     if (!callback.scope) {
2837                         callback.success(responseObject);
2838                     }
2839                     else {
2840
2841
2842                         callback.success.apply(callback.scope, [responseObject]);
2843                     }
2844                 }
2845             }
2846             else {
2847                 switch (httpStatus) {
2848
2849                     case 12002:
2850                     case 12029:
2851                     case 12030:
2852                     case 12031:
2853                     case 12152:
2854                     case 13030:
2855                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2856                         if (callback.failure) {
2857                             if (!callback.scope) {
2858                                 callback.failure(responseObject);
2859                             }
2860                             else {
2861                                 callback.failure.apply(callback.scope, [responseObject]);
2862                             }
2863                         }
2864                         break;
2865                     default:
2866                         responseObject = this.createResponseObject(o, callback.argument);
2867                         if (callback.failure) {
2868                             if (!callback.scope) {
2869                                 callback.failure(responseObject);
2870                             }
2871                             else {
2872                                 callback.failure.apply(callback.scope, [responseObject]);
2873                             }
2874                         }
2875                 }
2876             }
2877
2878             this.releaseObject(o);
2879             responseObject = null;
2880         },
2881
2882         createResponseObject:function(o, callbackArg)
2883         {
2884             var obj = {};
2885             var headerObj = {};
2886
2887             try
2888             {
2889                 var headerStr = o.conn.getAllResponseHeaders();
2890                 var header = headerStr.split('\n');
2891                 for (var i = 0; i < header.length; i++) {
2892                     var delimitPos = header[i].indexOf(':');
2893                     if (delimitPos != -1) {
2894                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2895                     }
2896                 }
2897             }
2898             catch(e) {
2899             }
2900
2901             obj.tId = o.tId;
2902             obj.status = o.conn.status;
2903             obj.statusText = o.conn.statusText;
2904             obj.getResponseHeader = headerObj;
2905             obj.getAllResponseHeaders = headerStr;
2906             obj.responseText = o.conn.responseText;
2907             obj.responseXML = o.conn.responseXML;
2908
2909             if (typeof callbackArg !== undefined) {
2910                 obj.argument = callbackArg;
2911             }
2912
2913             return obj;
2914         },
2915
2916         createExceptionObject:function(tId, callbackArg, isAbort)
2917         {
2918             var COMM_CODE = 0;
2919             var COMM_ERROR = 'communication failure';
2920             var ABORT_CODE = -1;
2921             var ABORT_ERROR = 'transaction aborted';
2922
2923             var obj = {};
2924
2925             obj.tId = tId;
2926             if (isAbort) {
2927                 obj.status = ABORT_CODE;
2928                 obj.statusText = ABORT_ERROR;
2929             }
2930             else {
2931                 obj.status = COMM_CODE;
2932                 obj.statusText = COMM_ERROR;
2933             }
2934
2935             if (callbackArg) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         initHeader:function(label, value, isDefault)
2943         {
2944             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2945
2946             if (headerObj[label] === undefined) {
2947                 headerObj[label] = value;
2948             }
2949             else {
2950
2951
2952                 headerObj[label] = value + "," + headerObj[label];
2953             }
2954
2955             if (isDefault) {
2956                 this.hasDefaultHeaders = true;
2957             }
2958             else {
2959                 this.hasHeaders = true;
2960             }
2961         },
2962
2963
2964         setHeader:function(o)
2965         {
2966             if (this.hasDefaultHeaders) {
2967                 for (var prop in this.defaultHeaders) {
2968                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2969                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2970                     }
2971                 }
2972             }
2973
2974             if (this.hasHeaders) {
2975                 for (var prop in this.headers) {
2976                     if (this.headers.hasOwnProperty(prop)) {
2977                         o.conn.setRequestHeader(prop, this.headers[prop]);
2978                     }
2979                 }
2980                 this.headers = {};
2981                 this.hasHeaders = false;
2982             }
2983         },
2984
2985         resetDefaultHeaders:function() {
2986             delete this.defaultHeaders;
2987             this.defaultHeaders = {};
2988             this.hasDefaultHeaders = false;
2989         },
2990
2991         abort:function(o, callback, isTimeout)
2992         {
2993             if(this.isCallInProgress(o)) {
2994                 o.conn.abort();
2995                 window.clearInterval(this.poll[o.tId]);
2996                 delete this.poll[o.tId];
2997                 if (isTimeout) {
2998                     delete this.timeout[o.tId];
2999                 }
3000
3001                 this.handleTransactionResponse(o, callback, true);
3002
3003                 return true;
3004             }
3005             else {
3006                 return false;
3007             }
3008         },
3009
3010
3011         isCallInProgress:function(o)
3012         {
3013             if (o && o.conn) {
3014                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3015             }
3016             else {
3017
3018                 return false;
3019             }
3020         },
3021
3022
3023         releaseObject:function(o)
3024         {
3025
3026             o.conn = null;
3027
3028             o = null;
3029         },
3030
3031         activeX:[
3032         'MSXML2.XMLHTTP.3.0',
3033         'MSXML2.XMLHTTP',
3034         'Microsoft.XMLHTTP'
3035         ]
3036
3037
3038     };
3039 })();/*
3040  * Portions of this file are based on pieces of Yahoo User Interface Library
3041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3042  * YUI licensed under the BSD License:
3043  * http://developer.yahoo.net/yui/license.txt
3044  * <script type="text/javascript">
3045  *
3046  */
3047
3048 Roo.lib.Region = function(t, r, b, l) {
3049     this.top = t;
3050     this[1] = t;
3051     this.right = r;
3052     this.bottom = b;
3053     this.left = l;
3054     this[0] = l;
3055 };
3056
3057
3058 Roo.lib.Region.prototype = {
3059     contains : function(region) {
3060         return ( region.left >= this.left &&
3061                  region.right <= this.right &&
3062                  region.top >= this.top &&
3063                  region.bottom <= this.bottom    );
3064
3065     },
3066
3067     getArea : function() {
3068         return ( (this.bottom - this.top) * (this.right - this.left) );
3069     },
3070
3071     intersect : function(region) {
3072         var t = Math.max(this.top, region.top);
3073         var r = Math.min(this.right, region.right);
3074         var b = Math.min(this.bottom, region.bottom);
3075         var l = Math.max(this.left, region.left);
3076
3077         if (b >= t && r >= l) {
3078             return new Roo.lib.Region(t, r, b, l);
3079         } else {
3080             return null;
3081         }
3082     },
3083     union : function(region) {
3084         var t = Math.min(this.top, region.top);
3085         var r = Math.max(this.right, region.right);
3086         var b = Math.max(this.bottom, region.bottom);
3087         var l = Math.min(this.left, region.left);
3088
3089         return new Roo.lib.Region(t, r, b, l);
3090     },
3091
3092     adjust : function(t, l, b, r) {
3093         this.top += t;
3094         this.left += l;
3095         this.right += r;
3096         this.bottom += b;
3097         return this;
3098     }
3099 };
3100
3101 Roo.lib.Region.getRegion = function(el) {
3102     var p = Roo.lib.Dom.getXY(el);
3103
3104     var t = p[1];
3105     var r = p[0] + el.offsetWidth;
3106     var b = p[1] + el.offsetHeight;
3107     var l = p[0];
3108
3109     return new Roo.lib.Region(t, r, b, l);
3110 };
3111 /*
3112  * Portions of this file are based on pieces of Yahoo User Interface Library
3113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3114  * YUI licensed under the BSD License:
3115  * http://developer.yahoo.net/yui/license.txt
3116  * <script type="text/javascript">
3117  *
3118  */
3119 //@@dep Roo.lib.Region
3120
3121
3122 Roo.lib.Point = function(x, y) {
3123     if (x instanceof Array) {
3124         y = x[1];
3125         x = x[0];
3126     }
3127     this.x = this.right = this.left = this[0] = x;
3128     this.y = this.top = this.bottom = this[1] = y;
3129 };
3130
3131 Roo.lib.Point.prototype = new Roo.lib.Region();
3132 /*
3133  * Portions of this file are based on pieces of Yahoo User Interface Library
3134  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3135  * YUI licensed under the BSD License:
3136  * http://developer.yahoo.net/yui/license.txt
3137  * <script type="text/javascript">
3138  *
3139  */
3140  
3141 (function() {   
3142
3143     Roo.lib.Anim = {
3144         scroll : function(el, args, duration, easing, cb, scope) {
3145             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3146         },
3147
3148         motion : function(el, args, duration, easing, cb, scope) {
3149             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3150         },
3151
3152         color : function(el, args, duration, easing, cb, scope) {
3153             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3154         },
3155
3156         run : function(el, args, duration, easing, cb, scope, type) {
3157             type = type || Roo.lib.AnimBase;
3158             if (typeof easing == "string") {
3159                 easing = Roo.lib.Easing[easing];
3160             }
3161             var anim = new type(el, args, duration, easing);
3162             anim.animateX(function() {
3163                 Roo.callback(cb, scope);
3164             });
3165             return anim;
3166         }
3167     };
3168 })();/*
3169  * Portions of this file are based on pieces of Yahoo User Interface Library
3170  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3171  * YUI licensed under the BSD License:
3172  * http://developer.yahoo.net/yui/license.txt
3173  * <script type="text/javascript">
3174  *
3175  */
3176
3177 (function() {    
3178     var libFlyweight;
3179     
3180     function fly(el) {
3181         if (!libFlyweight) {
3182             libFlyweight = new Roo.Element.Flyweight();
3183         }
3184         libFlyweight.dom = el;
3185         return libFlyweight;
3186     }
3187
3188     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3189     
3190    
3191     
3192     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3193         if (el) {
3194             this.init(el, attributes, duration, method);
3195         }
3196     };
3197
3198     Roo.lib.AnimBase.fly = fly;
3199     
3200     
3201     
3202     Roo.lib.AnimBase.prototype = {
3203
3204         toString: function() {
3205             var el = this.getEl();
3206             var id = el.id || el.tagName;
3207             return ("Anim " + id);
3208         },
3209
3210         patterns: {
3211             noNegatives:        /width|height|opacity|padding/i,
3212             offsetAttribute:  /^((width|height)|(top|left))$/,
3213             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3214             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3215         },
3216
3217
3218         doMethod: function(attr, start, end) {
3219             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3220         },
3221
3222
3223         setAttribute: function(attr, val, unit) {
3224             if (this.patterns.noNegatives.test(attr)) {
3225                 val = (val > 0) ? val : 0;
3226             }
3227
3228             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3229         },
3230
3231
3232         getAttribute: function(attr) {
3233             var el = this.getEl();
3234             var val = fly(el).getStyle(attr);
3235
3236             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3237                 return parseFloat(val);
3238             }
3239
3240             var a = this.patterns.offsetAttribute.exec(attr) || [];
3241             var pos = !!( a[3] );
3242             var box = !!( a[2] );
3243
3244
3245             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3246                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3247             } else {
3248                 val = 0;
3249             }
3250
3251             return val;
3252         },
3253
3254
3255         getDefaultUnit: function(attr) {
3256             if (this.patterns.defaultUnit.test(attr)) {
3257                 return 'px';
3258             }
3259
3260             return '';
3261         },
3262
3263         animateX : function(callback, scope) {
3264             var f = function() {
3265                 this.onComplete.removeListener(f);
3266                 if (typeof callback == "function") {
3267                     callback.call(scope || this, this);
3268                 }
3269             };
3270             this.onComplete.addListener(f, this);
3271             this.animate();
3272         },
3273
3274
3275         setRuntimeAttribute: function(attr) {
3276             var start;
3277             var end;
3278             var attributes = this.attributes;
3279
3280             this.runtimeAttributes[attr] = {};
3281
3282             var isset = function(prop) {
3283                 return (typeof prop !== 'undefined');
3284             };
3285
3286             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3287                 return false;
3288             }
3289
3290             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3291
3292
3293             if (isset(attributes[attr]['to'])) {
3294                 end = attributes[attr]['to'];
3295             } else if (isset(attributes[attr]['by'])) {
3296                 if (start.constructor == Array) {
3297                     end = [];
3298                     for (var i = 0, len = start.length; i < len; ++i) {
3299                         end[i] = start[i] + attributes[attr]['by'][i];
3300                     }
3301                 } else {
3302                     end = start + attributes[attr]['by'];
3303                 }
3304             }
3305
3306             this.runtimeAttributes[attr].start = start;
3307             this.runtimeAttributes[attr].end = end;
3308
3309
3310             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3311         },
3312
3313
3314         init: function(el, attributes, duration, method) {
3315
3316             var isAnimated = false;
3317
3318
3319             var startTime = null;
3320
3321
3322             var actualFrames = 0;
3323
3324
3325             el = Roo.getDom(el);
3326
3327
3328             this.attributes = attributes || {};
3329
3330
3331             this.duration = duration || 1;
3332
3333
3334             this.method = method || Roo.lib.Easing.easeNone;
3335
3336
3337             this.useSeconds = true;
3338
3339
3340             this.currentFrame = 0;
3341
3342
3343             this.totalFrames = Roo.lib.AnimMgr.fps;
3344
3345
3346             this.getEl = function() {
3347                 return el;
3348             };
3349
3350
3351             this.isAnimated = function() {
3352                 return isAnimated;
3353             };
3354
3355
3356             this.getStartTime = function() {
3357                 return startTime;
3358             };
3359
3360             this.runtimeAttributes = {};
3361
3362
3363             this.animate = function() {
3364                 if (this.isAnimated()) {
3365                     return false;
3366                 }
3367
3368                 this.currentFrame = 0;
3369
3370                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3371
3372                 Roo.lib.AnimMgr.registerElement(this);
3373             };
3374
3375
3376             this.stop = function(finish) {
3377                 if (finish) {
3378                     this.currentFrame = this.totalFrames;
3379                     this._onTween.fire();
3380                 }
3381                 Roo.lib.AnimMgr.stop(this);
3382             };
3383
3384             var onStart = function() {
3385                 this.onStart.fire();
3386
3387                 this.runtimeAttributes = {};
3388                 for (var attr in this.attributes) {
3389                     this.setRuntimeAttribute(attr);
3390                 }
3391
3392                 isAnimated = true;
3393                 actualFrames = 0;
3394                 startTime = new Date();
3395             };
3396
3397
3398             var onTween = function() {
3399                 var data = {
3400                     duration: new Date() - this.getStartTime(),
3401                     currentFrame: this.currentFrame
3402                 };
3403
3404                 data.toString = function() {
3405                     return (
3406                             'duration: ' + data.duration +
3407                             ', currentFrame: ' + data.currentFrame
3408                             );
3409                 };
3410
3411                 this.onTween.fire(data);
3412
3413                 var runtimeAttributes = this.runtimeAttributes;
3414
3415                 for (var attr in runtimeAttributes) {
3416                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3417                 }
3418
3419                 actualFrames += 1;
3420             };
3421
3422             var onComplete = function() {
3423                 var actual_duration = (new Date() - startTime) / 1000 ;
3424
3425                 var data = {
3426                     duration: actual_duration,
3427                     frames: actualFrames,
3428                     fps: actualFrames / actual_duration
3429                 };
3430
3431                 data.toString = function() {
3432                     return (
3433                             'duration: ' + data.duration +
3434                             ', frames: ' + data.frames +
3435                             ', fps: ' + data.fps
3436                             );
3437                 };
3438
3439                 isAnimated = false;
3440                 actualFrames = 0;
3441                 this.onComplete.fire(data);
3442             };
3443
3444
3445             this._onStart = new Roo.util.Event(this);
3446             this.onStart = new Roo.util.Event(this);
3447             this.onTween = new Roo.util.Event(this);
3448             this._onTween = new Roo.util.Event(this);
3449             this.onComplete = new Roo.util.Event(this);
3450             this._onComplete = new Roo.util.Event(this);
3451             this._onStart.addListener(onStart);
3452             this._onTween.addListener(onTween);
3453             this._onComplete.addListener(onComplete);
3454         }
3455     };
3456 })();
3457 /*
3458  * Portions of this file are based on pieces of Yahoo User Interface Library
3459  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3460  * YUI licensed under the BSD License:
3461  * http://developer.yahoo.net/yui/license.txt
3462  * <script type="text/javascript">
3463  *
3464  */
3465
3466 Roo.lib.AnimMgr = new function() {
3467
3468     var thread = null;
3469
3470
3471     var queue = [];
3472
3473
3474     var tweenCount = 0;
3475
3476
3477     this.fps = 1000;
3478
3479
3480     this.delay = 1;
3481
3482
3483     this.registerElement = function(tween) {
3484         queue[queue.length] = tween;
3485         tweenCount += 1;
3486         tween._onStart.fire();
3487         this.start();
3488     };
3489
3490
3491     this.unRegister = function(tween, index) {
3492         tween._onComplete.fire();
3493         index = index || getIndex(tween);
3494         if (index != -1) {
3495             queue.splice(index, 1);
3496         }
3497
3498         tweenCount -= 1;
3499         if (tweenCount <= 0) {
3500             this.stop();
3501         }
3502     };
3503
3504
3505     this.start = function() {
3506         if (thread === null) {
3507             thread = setInterval(this.run, this.delay);
3508         }
3509     };
3510
3511
3512     this.stop = function(tween) {
3513         if (!tween) {
3514             clearInterval(thread);
3515
3516             for (var i = 0, len = queue.length; i < len; ++i) {
3517                 if (queue[0].isAnimated()) {
3518                     this.unRegister(queue[0], 0);
3519                 }
3520             }
3521
3522             queue = [];
3523             thread = null;
3524             tweenCount = 0;
3525         }
3526         else {
3527             this.unRegister(tween);
3528         }
3529     };
3530
3531
3532     this.run = function() {
3533         for (var i = 0, len = queue.length; i < len; ++i) {
3534             var tween = queue[i];
3535             if (!tween || !tween.isAnimated()) {
3536                 continue;
3537             }
3538
3539             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3540             {
3541                 tween.currentFrame += 1;
3542
3543                 if (tween.useSeconds) {
3544                     correctFrame(tween);
3545                 }
3546                 tween._onTween.fire();
3547             }
3548             else {
3549                 Roo.lib.AnimMgr.stop(tween, i);
3550             }
3551         }
3552     };
3553
3554     var getIndex = function(anim) {
3555         for (var i = 0, len = queue.length; i < len; ++i) {
3556             if (queue[i] == anim) {
3557                 return i;
3558             }
3559         }
3560         return -1;
3561     };
3562
3563
3564     var correctFrame = function(tween) {
3565         var frames = tween.totalFrames;
3566         var frame = tween.currentFrame;
3567         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3568         var elapsed = (new Date() - tween.getStartTime());
3569         var tweak = 0;
3570
3571         if (elapsed < tween.duration * 1000) {
3572             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3573         } else {
3574             tweak = frames - (frame + 1);
3575         }
3576         if (tweak > 0 && isFinite(tweak)) {
3577             if (tween.currentFrame + tweak >= frames) {
3578                 tweak = frames - (frame + 1);
3579             }
3580
3581             tween.currentFrame += tweak;
3582         }
3583     };
3584 };
3585
3586     /*
3587  * Portions of this file are based on pieces of Yahoo User Interface Library
3588  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3589  * YUI licensed under the BSD License:
3590  * http://developer.yahoo.net/yui/license.txt
3591  * <script type="text/javascript">
3592  *
3593  */
3594 Roo.lib.Bezier = new function() {
3595
3596         this.getPosition = function(points, t) {
3597             var n = points.length;
3598             var tmp = [];
3599
3600             for (var i = 0; i < n; ++i) {
3601                 tmp[i] = [points[i][0], points[i][1]];
3602             }
3603
3604             for (var j = 1; j < n; ++j) {
3605                 for (i = 0; i < n - j; ++i) {
3606                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3607                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3608                 }
3609             }
3610
3611             return [ tmp[0][0], tmp[0][1] ];
3612
3613         };
3614     };/*
3615  * Portions of this file are based on pieces of Yahoo User Interface Library
3616  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3617  * YUI licensed under the BSD License:
3618  * http://developer.yahoo.net/yui/license.txt
3619  * <script type="text/javascript">
3620  *
3621  */
3622 (function() {
3623
3624     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3625         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3626     };
3627
3628     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3629
3630     var fly = Roo.lib.AnimBase.fly;
3631     var Y = Roo.lib;
3632     var superclass = Y.ColorAnim.superclass;
3633     var proto = Y.ColorAnim.prototype;
3634
3635     proto.toString = function() {
3636         var el = this.getEl();
3637         var id = el.id || el.tagName;
3638         return ("ColorAnim " + id);
3639     };
3640
3641     proto.patterns.color = /color$/i;
3642     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3643     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3644     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3645     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3646
3647
3648     proto.parseColor = function(s) {
3649         if (s.length == 3) {
3650             return s;
3651         }
3652
3653         var c = this.patterns.hex.exec(s);
3654         if (c && c.length == 4) {
3655             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3656         }
3657
3658         c = this.patterns.rgb.exec(s);
3659         if (c && c.length == 4) {
3660             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3661         }
3662
3663         c = this.patterns.hex3.exec(s);
3664         if (c && c.length == 4) {
3665             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3666         }
3667
3668         return null;
3669     };
3670     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3671     proto.getAttribute = function(attr) {
3672         var el = this.getEl();
3673         if (this.patterns.color.test(attr)) {
3674             var val = fly(el).getStyle(attr);
3675
3676             if (this.patterns.transparent.test(val)) {
3677                 var parent = el.parentNode;
3678                 val = fly(parent).getStyle(attr);
3679
3680                 while (parent && this.patterns.transparent.test(val)) {
3681                     parent = parent.parentNode;
3682                     val = fly(parent).getStyle(attr);
3683                     if (parent.tagName.toUpperCase() == 'HTML') {
3684                         val = '#fff';
3685                     }
3686                 }
3687             }
3688         } else {
3689             val = superclass.getAttribute.call(this, attr);
3690         }
3691
3692         return val;
3693     };
3694     proto.getAttribute = function(attr) {
3695         var el = this.getEl();
3696         if (this.patterns.color.test(attr)) {
3697             var val = fly(el).getStyle(attr);
3698
3699             if (this.patterns.transparent.test(val)) {
3700                 var parent = el.parentNode;
3701                 val = fly(parent).getStyle(attr);
3702
3703                 while (parent && this.patterns.transparent.test(val)) {
3704                     parent = parent.parentNode;
3705                     val = fly(parent).getStyle(attr);
3706                     if (parent.tagName.toUpperCase() == 'HTML') {
3707                         val = '#fff';
3708                     }
3709                 }
3710             }
3711         } else {
3712             val = superclass.getAttribute.call(this, attr);
3713         }
3714
3715         return val;
3716     };
3717
3718     proto.doMethod = function(attr, start, end) {
3719         var val;
3720
3721         if (this.patterns.color.test(attr)) {
3722             val = [];
3723             for (var i = 0, len = start.length; i < len; ++i) {
3724                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3725             }
3726
3727             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3728         }
3729         else {
3730             val = superclass.doMethod.call(this, attr, start, end);
3731         }
3732
3733         return val;
3734     };
3735
3736     proto.setRuntimeAttribute = function(attr) {
3737         superclass.setRuntimeAttribute.call(this, attr);
3738
3739         if (this.patterns.color.test(attr)) {
3740             var attributes = this.attributes;
3741             var start = this.parseColor(this.runtimeAttributes[attr].start);
3742             var end = this.parseColor(this.runtimeAttributes[attr].end);
3743
3744             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3745                 end = this.parseColor(attributes[attr].by);
3746
3747                 for (var i = 0, len = start.length; i < len; ++i) {
3748                     end[i] = start[i] + end[i];
3749                 }
3750             }
3751
3752             this.runtimeAttributes[attr].start = start;
3753             this.runtimeAttributes[attr].end = end;
3754         }
3755     };
3756 })();
3757
3758 /*
3759  * Portions of this file are based on pieces of Yahoo User Interface Library
3760  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3761  * YUI licensed under the BSD License:
3762  * http://developer.yahoo.net/yui/license.txt
3763  * <script type="text/javascript">
3764  *
3765  */
3766 Roo.lib.Easing = {
3767
3768
3769     easeNone: function (t, b, c, d) {
3770         return c * t / d + b;
3771     },
3772
3773
3774     easeIn: function (t, b, c, d) {
3775         return c * (t /= d) * t + b;
3776     },
3777
3778
3779     easeOut: function (t, b, c, d) {
3780         return -c * (t /= d) * (t - 2) + b;
3781     },
3782
3783
3784     easeBoth: function (t, b, c, d) {
3785         if ((t /= d / 2) < 1) {
3786             return c / 2 * t * t + b;
3787         }
3788
3789         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3790     },
3791
3792
3793     easeInStrong: function (t, b, c, d) {
3794         return c * (t /= d) * t * t * t + b;
3795     },
3796
3797
3798     easeOutStrong: function (t, b, c, d) {
3799         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3800     },
3801
3802
3803     easeBothStrong: function (t, b, c, d) {
3804         if ((t /= d / 2) < 1) {
3805             return c / 2 * t * t * t * t + b;
3806         }
3807
3808         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3809     },
3810
3811
3812
3813     elasticIn: function (t, b, c, d, a, p) {
3814         if (t == 0) {
3815             return b;
3816         }
3817         if ((t /= d) == 1) {
3818             return b + c;
3819         }
3820         if (!p) {
3821             p = d * .3;
3822         }
3823
3824         if (!a || a < Math.abs(c)) {
3825             a = c;
3826             var s = p / 4;
3827         }
3828         else {
3829             var s = p / (2 * Math.PI) * Math.asin(c / a);
3830         }
3831
3832         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3833     },
3834
3835
3836     elasticOut: function (t, b, c, d, a, p) {
3837         if (t == 0) {
3838             return b;
3839         }
3840         if ((t /= d) == 1) {
3841             return b + c;
3842         }
3843         if (!p) {
3844             p = d * .3;
3845         }
3846
3847         if (!a || a < Math.abs(c)) {
3848             a = c;
3849             var s = p / 4;
3850         }
3851         else {
3852             var s = p / (2 * Math.PI) * Math.asin(c / a);
3853         }
3854
3855         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3856     },
3857
3858
3859     elasticBoth: function (t, b, c, d, a, p) {
3860         if (t == 0) {
3861             return b;
3862         }
3863
3864         if ((t /= d / 2) == 2) {
3865             return b + c;
3866         }
3867
3868         if (!p) {
3869             p = d * (.3 * 1.5);
3870         }
3871
3872         if (!a || a < Math.abs(c)) {
3873             a = c;
3874             var s = p / 4;
3875         }
3876         else {
3877             var s = p / (2 * Math.PI) * Math.asin(c / a);
3878         }
3879
3880         if (t < 1) {
3881             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3882                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3883         }
3884         return a * Math.pow(2, -10 * (t -= 1)) *
3885                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3886     },
3887
3888
3889
3890     backIn: function (t, b, c, d, s) {
3891         if (typeof s == 'undefined') {
3892             s = 1.70158;
3893         }
3894         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3895     },
3896
3897
3898     backOut: function (t, b, c, d, s) {
3899         if (typeof s == 'undefined') {
3900             s = 1.70158;
3901         }
3902         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3903     },
3904
3905
3906     backBoth: function (t, b, c, d, s) {
3907         if (typeof s == 'undefined') {
3908             s = 1.70158;
3909         }
3910
3911         if ((t /= d / 2 ) < 1) {
3912             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3913         }
3914         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3915     },
3916
3917
3918     bounceIn: function (t, b, c, d) {
3919         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3920     },
3921
3922
3923     bounceOut: function (t, b, c, d) {
3924         if ((t /= d) < (1 / 2.75)) {
3925             return c * (7.5625 * t * t) + b;
3926         } else if (t < (2 / 2.75)) {
3927             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3928         } else if (t < (2.5 / 2.75)) {
3929             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3930         }
3931         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3932     },
3933
3934
3935     bounceBoth: function (t, b, c, d) {
3936         if (t < d / 2) {
3937             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3938         }
3939         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3940     }
3941 };/*
3942  * Portions of this file are based on pieces of Yahoo User Interface Library
3943  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3944  * YUI licensed under the BSD License:
3945  * http://developer.yahoo.net/yui/license.txt
3946  * <script type="text/javascript">
3947  *
3948  */
3949     (function() {
3950         Roo.lib.Motion = function(el, attributes, duration, method) {
3951             if (el) {
3952                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3953             }
3954         };
3955
3956         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3957
3958
3959         var Y = Roo.lib;
3960         var superclass = Y.Motion.superclass;
3961         var proto = Y.Motion.prototype;
3962
3963         proto.toString = function() {
3964             var el = this.getEl();
3965             var id = el.id || el.tagName;
3966             return ("Motion " + id);
3967         };
3968
3969         proto.patterns.points = /^points$/i;
3970
3971         proto.setAttribute = function(attr, val, unit) {
3972             if (this.patterns.points.test(attr)) {
3973                 unit = unit || 'px';
3974                 superclass.setAttribute.call(this, 'left', val[0], unit);
3975                 superclass.setAttribute.call(this, 'top', val[1], unit);
3976             } else {
3977                 superclass.setAttribute.call(this, attr, val, unit);
3978             }
3979         };
3980
3981         proto.getAttribute = function(attr) {
3982             if (this.patterns.points.test(attr)) {
3983                 var val = [
3984                         superclass.getAttribute.call(this, 'left'),
3985                         superclass.getAttribute.call(this, 'top')
3986                         ];
3987             } else {
3988                 val = superclass.getAttribute.call(this, attr);
3989             }
3990
3991             return val;
3992         };
3993
3994         proto.doMethod = function(attr, start, end) {
3995             var val = null;
3996
3997             if (this.patterns.points.test(attr)) {
3998                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3999                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4000             } else {
4001                 val = superclass.doMethod.call(this, attr, start, end);
4002             }
4003             return val;
4004         };
4005
4006         proto.setRuntimeAttribute = function(attr) {
4007             if (this.patterns.points.test(attr)) {
4008                 var el = this.getEl();
4009                 var attributes = this.attributes;
4010                 var start;
4011                 var control = attributes['points']['control'] || [];
4012                 var end;
4013                 var i, len;
4014
4015                 if (control.length > 0 && !(control[0] instanceof Array)) {
4016                     control = [control];
4017                 } else {
4018                     var tmp = [];
4019                     for (i = 0,len = control.length; i < len; ++i) {
4020                         tmp[i] = control[i];
4021                     }
4022                     control = tmp;
4023                 }
4024
4025                 Roo.fly(el).position();
4026
4027                 if (isset(attributes['points']['from'])) {
4028                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4029                 }
4030                 else {
4031                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4032                 }
4033
4034                 start = this.getAttribute('points');
4035
4036
4037                 if (isset(attributes['points']['to'])) {
4038                     end = translateValues.call(this, attributes['points']['to'], start);
4039
4040                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4041                     for (i = 0,len = control.length; i < len; ++i) {
4042                         control[i] = translateValues.call(this, control[i], start);
4043                     }
4044
4045
4046                 } else if (isset(attributes['points']['by'])) {
4047                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4048
4049                     for (i = 0,len = control.length; i < len; ++i) {
4050                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4051                     }
4052                 }
4053
4054                 this.runtimeAttributes[attr] = [start];
4055
4056                 if (control.length > 0) {
4057                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4058                 }
4059
4060                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4061             }
4062             else {
4063                 superclass.setRuntimeAttribute.call(this, attr);
4064             }
4065         };
4066
4067         var translateValues = function(val, start) {
4068             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4069             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4070
4071             return val;
4072         };
4073
4074         var isset = function(prop) {
4075             return (typeof prop !== 'undefined');
4076         };
4077     })();
4078 /*
4079  * Portions of this file are based on pieces of Yahoo User Interface Library
4080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4081  * YUI licensed under the BSD License:
4082  * http://developer.yahoo.net/yui/license.txt
4083  * <script type="text/javascript">
4084  *
4085  */
4086     (function() {
4087         Roo.lib.Scroll = function(el, attributes, duration, method) {
4088             if (el) {
4089                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4090             }
4091         };
4092
4093         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4094
4095
4096         var Y = Roo.lib;
4097         var superclass = Y.Scroll.superclass;
4098         var proto = Y.Scroll.prototype;
4099
4100         proto.toString = function() {
4101             var el = this.getEl();
4102             var id = el.id || el.tagName;
4103             return ("Scroll " + id);
4104         };
4105
4106         proto.doMethod = function(attr, start, end) {
4107             var val = null;
4108
4109             if (attr == 'scroll') {
4110                 val = [
4111                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4112                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4113                         ];
4114
4115             } else {
4116                 val = superclass.doMethod.call(this, attr, start, end);
4117             }
4118             return val;
4119         };
4120
4121         proto.getAttribute = function(attr) {
4122             var val = null;
4123             var el = this.getEl();
4124
4125             if (attr == 'scroll') {
4126                 val = [ el.scrollLeft, el.scrollTop ];
4127             } else {
4128                 val = superclass.getAttribute.call(this, attr);
4129             }
4130
4131             return val;
4132         };
4133
4134         proto.setAttribute = function(attr, val, unit) {
4135             var el = this.getEl();
4136
4137             if (attr == 'scroll') {
4138                 el.scrollLeft = val[0];
4139                 el.scrollTop = val[1];
4140             } else {
4141                 superclass.setAttribute.call(this, attr, val, unit);
4142             }
4143         };
4144     })();
4145 /*
4146  * Based on:
4147  * Ext JS Library 1.1.1
4148  * Copyright(c) 2006-2007, Ext JS, LLC.
4149  *
4150  * Originally Released Under LGPL - original licence link has changed is not relivant.
4151  *
4152  * Fork - LGPL
4153  * <script type="text/javascript">
4154  */
4155
4156
4157 // nasty IE9 hack - what a pile of crap that is..
4158
4159  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4160     Range.prototype.createContextualFragment = function (html) {
4161         var doc = window.document;
4162         var container = doc.createElement("div");
4163         container.innerHTML = html;
4164         var frag = doc.createDocumentFragment(), n;
4165         while ((n = container.firstChild)) {
4166             frag.appendChild(n);
4167         }
4168         return frag;
4169     };
4170 }
4171
4172 /**
4173  * @class Roo.DomHelper
4174  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4175  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4176  * @singleton
4177  */
4178 Roo.DomHelper = function(){
4179     var tempTableEl = null;
4180     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4181     var tableRe = /^table|tbody|tr|td$/i;
4182     var xmlns = {};
4183     // build as innerHTML where available
4184     /** @ignore */
4185     var createHtml = function(o){
4186         if(typeof o == 'string'){
4187             return o;
4188         }
4189         var b = "";
4190         if(!o.tag){
4191             o.tag = "div";
4192         }
4193         b += "<" + o.tag;
4194         for(var attr in o){
4195             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4196             if(attr == "style"){
4197                 var s = o["style"];
4198                 if(typeof s == "function"){
4199                     s = s.call();
4200                 }
4201                 if(typeof s == "string"){
4202                     b += ' style="' + s + '"';
4203                 }else if(typeof s == "object"){
4204                     b += ' style="';
4205                     for(var key in s){
4206                         if(typeof s[key] != "function"){
4207                             b += key + ":" + s[key] + ";";
4208                         }
4209                     }
4210                     b += '"';
4211                 }
4212             }else{
4213                 if(attr == "cls"){
4214                     b += ' class="' + o["cls"] + '"';
4215                 }else if(attr == "htmlFor"){
4216                     b += ' for="' + o["htmlFor"] + '"';
4217                 }else{
4218                     b += " " + attr + '="' + o[attr] + '"';
4219                 }
4220             }
4221         }
4222         if(emptyTags.test(o.tag)){
4223             b += "/>";
4224         }else{
4225             b += ">";
4226             var cn = o.children || o.cn;
4227             if(cn){
4228                 //http://bugs.kde.org/show_bug.cgi?id=71506
4229                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4230                     for(var i = 0, len = cn.length; i < len; i++) {
4231                         b += createHtml(cn[i], b);
4232                     }
4233                 }else{
4234                     b += createHtml(cn, b);
4235                 }
4236             }
4237             if(o.html){
4238                 b += o.html;
4239             }
4240             b += "</" + o.tag + ">";
4241         }
4242         return b;
4243     };
4244
4245     // build as dom
4246     /** @ignore */
4247     var createDom = function(o, parentNode){
4248          
4249         // defininition craeted..
4250         var ns = false;
4251         if (o.ns && o.ns != 'html') {
4252                
4253             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4254                 xmlns[o.ns] = o.xmlns;
4255                 ns = o.xmlns;
4256             }
4257             if (typeof(xmlns[o.ns]) == 'undefined') {
4258                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4259             }
4260             ns = xmlns[o.ns];
4261         }
4262         
4263         
4264         if (typeof(o) == 'string') {
4265             return parentNode.appendChild(document.createTextNode(o));
4266         }
4267         o.tag = o.tag || div;
4268         if (o.ns && Roo.isIE) {
4269             ns = false;
4270             o.tag = o.ns + ':' + o.tag;
4271             
4272         }
4273         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4274         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4275         for(var attr in o){
4276             
4277             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4278                     attr == "style" || typeof o[attr] == "function") { continue; }
4279                     
4280             if(attr=="cls" && Roo.isIE){
4281                 el.className = o["cls"];
4282             }else{
4283                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4284                 else { 
4285                     el[attr] = o[attr];
4286                 }
4287             }
4288         }
4289         Roo.DomHelper.applyStyles(el, o.style);
4290         var cn = o.children || o.cn;
4291         if(cn){
4292             //http://bugs.kde.org/show_bug.cgi?id=71506
4293              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4294                 for(var i = 0, len = cn.length; i < len; i++) {
4295                     createDom(cn[i], el);
4296                 }
4297             }else{
4298                 createDom(cn, el);
4299             }
4300         }
4301         if(o.html){
4302             el.innerHTML = o.html;
4303         }
4304         if(parentNode){
4305            parentNode.appendChild(el);
4306         }
4307         return el;
4308     };
4309
4310     var ieTable = function(depth, s, h, e){
4311         tempTableEl.innerHTML = [s, h, e].join('');
4312         var i = -1, el = tempTableEl;
4313         while(++i < depth){
4314             el = el.firstChild;
4315         }
4316         return el;
4317     };
4318
4319     // kill repeat to save bytes
4320     var ts = '<table>',
4321         te = '</table>',
4322         tbs = ts+'<tbody>',
4323         tbe = '</tbody>'+te,
4324         trs = tbs + '<tr>',
4325         tre = '</tr>'+tbe;
4326
4327     /**
4328      * @ignore
4329      * Nasty code for IE's broken table implementation
4330      */
4331     var insertIntoTable = function(tag, where, el, html){
4332         if(!tempTableEl){
4333             tempTableEl = document.createElement('div');
4334         }
4335         var node;
4336         var before = null;
4337         if(tag == 'td'){
4338             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4339                 return;
4340             }
4341             if(where == 'beforebegin'){
4342                 before = el;
4343                 el = el.parentNode;
4344             } else{
4345                 before = el.nextSibling;
4346                 el = el.parentNode;
4347             }
4348             node = ieTable(4, trs, html, tre);
4349         }
4350         else if(tag == 'tr'){
4351             if(where == 'beforebegin'){
4352                 before = el;
4353                 el = el.parentNode;
4354                 node = ieTable(3, tbs, html, tbe);
4355             } else if(where == 'afterend'){
4356                 before = el.nextSibling;
4357                 el = el.parentNode;
4358                 node = ieTable(3, tbs, html, tbe);
4359             } else{ // INTO a TR
4360                 if(where == 'afterbegin'){
4361                     before = el.firstChild;
4362                 }
4363                 node = ieTable(4, trs, html, tre);
4364             }
4365         } else if(tag == 'tbody'){
4366             if(where == 'beforebegin'){
4367                 before = el;
4368                 el = el.parentNode;
4369                 node = ieTable(2, ts, html, te);
4370             } else if(where == 'afterend'){
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373                 node = ieTable(2, ts, html, te);
4374             } else{
4375                 if(where == 'afterbegin'){
4376                     before = el.firstChild;
4377                 }
4378                 node = ieTable(3, tbs, html, tbe);
4379             }
4380         } else{ // TABLE
4381             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4382                 return;
4383             }
4384             if(where == 'afterbegin'){
4385                 before = el.firstChild;
4386             }
4387             node = ieTable(2, ts, html, te);
4388         }
4389         el.insertBefore(node, before);
4390         return node;
4391     };
4392
4393     return {
4394     /** True to force the use of DOM instead of html fragments @type Boolean */
4395     useDom : false,
4396
4397     /**
4398      * Returns the markup for the passed Element(s) config
4399      * @param {Object} o The Dom object spec (and children)
4400      * @return {String}
4401      */
4402     markup : function(o){
4403         return createHtml(o);
4404     },
4405
4406     /**
4407      * Applies a style specification to an element
4408      * @param {String/HTMLElement} el The element to apply styles to
4409      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4410      * a function which returns such a specification.
4411      */
4412     applyStyles : function(el, styles){
4413         if(styles){
4414            el = Roo.fly(el);
4415            if(typeof styles == "string"){
4416                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4417                var matches;
4418                while ((matches = re.exec(styles)) != null){
4419                    el.setStyle(matches[1], matches[2]);
4420                }
4421            }else if (typeof styles == "object"){
4422                for (var style in styles){
4423                   el.setStyle(style, styles[style]);
4424                }
4425            }else if (typeof styles == "function"){
4426                 Roo.DomHelper.applyStyles(el, styles.call());
4427            }
4428         }
4429     },
4430
4431     /**
4432      * Inserts an HTML fragment into the Dom
4433      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4434      * @param {HTMLElement} el The context element
4435      * @param {String} html The HTML fragmenet
4436      * @return {HTMLElement} The new node
4437      */
4438     insertHtml : function(where, el, html){
4439         where = where.toLowerCase();
4440         if(el.insertAdjacentHTML){
4441             if(tableRe.test(el.tagName)){
4442                 var rs;
4443                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4444                     return rs;
4445                 }
4446             }
4447             switch(where){
4448                 case "beforebegin":
4449                     el.insertAdjacentHTML('BeforeBegin', html);
4450                     return el.previousSibling;
4451                 case "afterbegin":
4452                     el.insertAdjacentHTML('AfterBegin', html);
4453                     return el.firstChild;
4454                 case "beforeend":
4455                     el.insertAdjacentHTML('BeforeEnd', html);
4456                     return el.lastChild;
4457                 case "afterend":
4458                     el.insertAdjacentHTML('AfterEnd', html);
4459                     return el.nextSibling;
4460             }
4461             throw 'Illegal insertion point -> "' + where + '"';
4462         }
4463         var range = el.ownerDocument.createRange();
4464         var frag;
4465         switch(where){
4466              case "beforebegin":
4467                 range.setStartBefore(el);
4468                 frag = range.createContextualFragment(html);
4469                 el.parentNode.insertBefore(frag, el);
4470                 return el.previousSibling;
4471              case "afterbegin":
4472                 if(el.firstChild){
4473                     range.setStartBefore(el.firstChild);
4474                     frag = range.createContextualFragment(html);
4475                     el.insertBefore(frag, el.firstChild);
4476                     return el.firstChild;
4477                 }else{
4478                     el.innerHTML = html;
4479                     return el.firstChild;
4480                 }
4481             case "beforeend":
4482                 if(el.lastChild){
4483                     range.setStartAfter(el.lastChild);
4484                     frag = range.createContextualFragment(html);
4485                     el.appendChild(frag);
4486                     return el.lastChild;
4487                 }else{
4488                     el.innerHTML = html;
4489                     return el.lastChild;
4490                 }
4491             case "afterend":
4492                 range.setStartAfter(el);
4493                 frag = range.createContextualFragment(html);
4494                 el.parentNode.insertBefore(frag, el.nextSibling);
4495                 return el.nextSibling;
4496             }
4497             throw 'Illegal insertion point -> "' + where + '"';
4498     },
4499
4500     /**
4501      * Creates new Dom element(s) and inserts them before el
4502      * @param {String/HTMLElement/Element} el The context element
4503      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4504      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4505      * @return {HTMLElement/Roo.Element} The new node
4506      */
4507     insertBefore : function(el, o, returnElement){
4508         return this.doInsert(el, o, returnElement, "beforeBegin");
4509     },
4510
4511     /**
4512      * Creates new Dom element(s) and inserts them after el
4513      * @param {String/HTMLElement/Element} el The context element
4514      * @param {Object} o The Dom object spec (and children)
4515      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4516      * @return {HTMLElement/Roo.Element} The new node
4517      */
4518     insertAfter : function(el, o, returnElement){
4519         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4520     },
4521
4522     /**
4523      * Creates new Dom element(s) and inserts them as the first child of el
4524      * @param {String/HTMLElement/Element} el The context element
4525      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4526      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4527      * @return {HTMLElement/Roo.Element} The new node
4528      */
4529     insertFirst : function(el, o, returnElement){
4530         return this.doInsert(el, o, returnElement, "afterBegin");
4531     },
4532
4533     // private
4534     doInsert : function(el, o, returnElement, pos, sibling){
4535         el = Roo.getDom(el);
4536         var newNode;
4537         if(this.useDom || o.ns){
4538             newNode = createDom(o, null);
4539             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4540         }else{
4541             var html = createHtml(o);
4542             newNode = this.insertHtml(pos, el, html);
4543         }
4544         return returnElement ? Roo.get(newNode, true) : newNode;
4545     },
4546
4547     /**
4548      * Creates new Dom element(s) and appends them to el
4549      * @param {String/HTMLElement/Element} el The context element
4550      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4551      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4552      * @return {HTMLElement/Roo.Element} The new node
4553      */
4554     append : function(el, o, returnElement){
4555         el = Roo.getDom(el);
4556         var newNode;
4557         if(this.useDom || o.ns){
4558             newNode = createDom(o, null);
4559             el.appendChild(newNode);
4560         }else{
4561             var html = createHtml(o);
4562             newNode = this.insertHtml("beforeEnd", el, html);
4563         }
4564         return returnElement ? Roo.get(newNode, true) : newNode;
4565     },
4566
4567     /**
4568      * Creates new Dom element(s) and overwrites the contents of el with them
4569      * @param {String/HTMLElement/Element} el The context element
4570      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4571      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4572      * @return {HTMLElement/Roo.Element} The new node
4573      */
4574     overwrite : function(el, o, returnElement){
4575         el = Roo.getDom(el);
4576         if (o.ns) {
4577           
4578             while (el.childNodes.length) {
4579                 el.removeChild(el.firstChild);
4580             }
4581             createDom(o, el);
4582         } else {
4583             el.innerHTML = createHtml(o);   
4584         }
4585         
4586         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4587     },
4588
4589     /**
4590      * Creates a new Roo.DomHelper.Template from the Dom object spec
4591      * @param {Object} o The Dom object spec (and children)
4592      * @return {Roo.DomHelper.Template} The new template
4593      */
4594     createTemplate : function(o){
4595         var html = createHtml(o);
4596         return new Roo.Template(html);
4597     }
4598     };
4599 }();
4600 /*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611 /**
4612 * @class Roo.Template
4613 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4614 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4615 * Usage:
4616 <pre><code>
4617 var t = new Roo.Template({
4618     html :  '&lt;div name="{id}"&gt;' + 
4619         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4620         '&lt;/div&gt;',
4621     myformat: function (value, allValues) {
4622         return 'XX' + value;
4623     }
4624 });
4625 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4626 </code></pre>
4627 * For more information see this blog post with examples:
4628 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4629      - Create Elements using DOM, HTML fragments and Templates</a>. 
4630 * @constructor
4631 * @param {Object} cfg - Configuration object.
4632 */
4633 Roo.Template = function(cfg){
4634     // BC!
4635     if(cfg instanceof Array){
4636         cfg = cfg.join("");
4637     }else if(arguments.length > 1){
4638         cfg = Array.prototype.join.call(arguments, "");
4639     }
4640     
4641     
4642     if (typeof(cfg) == 'object') {
4643         Roo.apply(this,cfg)
4644     } else {
4645         // bc
4646         this.html = cfg;
4647     }
4648     if (this.url) {
4649         this.load();
4650     }
4651     
4652 };
4653 Roo.Template.prototype = {
4654     
4655     /**
4656      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4657      *                    it should be fixed so that template is observable...
4658      */
4659     url : false,
4660     /**
4661      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4662      */
4663     html : '',
4664     /**
4665      * Returns an HTML fragment of this template with the specified values applied.
4666      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4667      * @return {String} The HTML fragment
4668      */
4669     applyTemplate : function(values){
4670         try {
4671            
4672             if(this.compiled){
4673                 return this.compiled(values);
4674             }
4675             var useF = this.disableFormats !== true;
4676             var fm = Roo.util.Format, tpl = this;
4677             var fn = function(m, name, format, args){
4678                 if(format && useF){
4679                     if(format.substr(0, 5) == "this."){
4680                         return tpl.call(format.substr(5), values[name], values);
4681                     }else{
4682                         if(args){
4683                             // quoted values are required for strings in compiled templates, 
4684                             // but for non compiled we need to strip them
4685                             // quoted reversed for jsmin
4686                             var re = /^\s*['"](.*)["']\s*$/;
4687                             args = args.split(',');
4688                             for(var i = 0, len = args.length; i < len; i++){
4689                                 args[i] = args[i].replace(re, "$1");
4690                             }
4691                             args = [values[name]].concat(args);
4692                         }else{
4693                             args = [values[name]];
4694                         }
4695                         return fm[format].apply(fm, args);
4696                     }
4697                 }else{
4698                     return values[name] !== undefined ? values[name] : "";
4699                 }
4700             };
4701             return this.html.replace(this.re, fn);
4702         } catch (e) {
4703             Roo.log(e);
4704             throw e;
4705         }
4706          
4707     },
4708     
4709     loading : false,
4710       
4711     load : function ()
4712     {
4713          
4714         if (this.loading) {
4715             return;
4716         }
4717         var _t = this;
4718         
4719         this.loading = true;
4720         this.compiled = false;
4721         
4722         var cx = new Roo.data.Connection();
4723         cx.request({
4724             url : this.url,
4725             method : 'GET',
4726             success : function (response) {
4727                 _t.loading = false;
4728                 _t.html = response.responseText;
4729                 _t.url = false;
4730                 _t.compile();
4731              },
4732             failure : function(response) {
4733                 Roo.log("Template failed to load from " + _t.url);
4734                 _t.loading = false;
4735             }
4736         });
4737     },
4738
4739     /**
4740      * Sets the HTML used as the template and optionally compiles it.
4741      * @param {String} html
4742      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4743      * @return {Roo.Template} this
4744      */
4745     set : function(html, compile){
4746         this.html = html;
4747         this.compiled = null;
4748         if(compile){
4749             this.compile();
4750         }
4751         return this;
4752     },
4753     
4754     /**
4755      * True to disable format functions (defaults to false)
4756      * @type Boolean
4757      */
4758     disableFormats : false,
4759     
4760     /**
4761     * The regular expression used to match template variables 
4762     * @type RegExp
4763     * @property 
4764     */
4765     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4766     
4767     /**
4768      * Compiles the template into an internal function, eliminating the RegEx overhead.
4769      * @return {Roo.Template} this
4770      */
4771     compile : function(){
4772         var fm = Roo.util.Format;
4773         var useF = this.disableFormats !== true;
4774         var sep = Roo.isGecko ? "+" : ",";
4775         var fn = function(m, name, format, args){
4776             if(format && useF){
4777                 args = args ? ',' + args : "";
4778                 if(format.substr(0, 5) != "this."){
4779                     format = "fm." + format + '(';
4780                 }else{
4781                     format = 'this.call("'+ format.substr(5) + '", ';
4782                     args = ", values";
4783                 }
4784             }else{
4785                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4786             }
4787             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4788         };
4789         var body;
4790         // branched to use + in gecko and [].join() in others
4791         if(Roo.isGecko){
4792             body = "this.compiled = function(values){ return '" +
4793                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4794                     "';};";
4795         }else{
4796             body = ["this.compiled = function(values){ return ['"];
4797             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4798             body.push("'].join('');};");
4799             body = body.join('');
4800         }
4801         /**
4802          * eval:var:values
4803          * eval:var:fm
4804          */
4805         eval(body);
4806         return this;
4807     },
4808     
4809     // private function used to call members
4810     call : function(fnName, value, allValues){
4811         return this[fnName](value, allValues);
4812     },
4813     
4814     /**
4815      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4816      * @param {String/HTMLElement/Roo.Element} el The context element
4817      * @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'})
4818      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4819      * @return {HTMLElement/Roo.Element} The new node or Element
4820      */
4821     insertFirst: function(el, values, returnElement){
4822         return this.doInsert('afterBegin', el, values, returnElement);
4823     },
4824
4825     /**
4826      * Applies the supplied values to the template and inserts the new node(s) before el.
4827      * @param {String/HTMLElement/Roo.Element} el The context element
4828      * @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'})
4829      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4830      * @return {HTMLElement/Roo.Element} The new node or Element
4831      */
4832     insertBefore: function(el, values, returnElement){
4833         return this.doInsert('beforeBegin', el, values, returnElement);
4834     },
4835
4836     /**
4837      * Applies the supplied values to the template and inserts the new node(s) after el.
4838      * @param {String/HTMLElement/Roo.Element} el The context element
4839      * @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'})
4840      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4841      * @return {HTMLElement/Roo.Element} The new node or Element
4842      */
4843     insertAfter : function(el, values, returnElement){
4844         return this.doInsert('afterEnd', el, values, returnElement);
4845     },
4846     
4847     /**
4848      * Applies the supplied values to the template and appends the new node(s) to el.
4849      * @param {String/HTMLElement/Roo.Element} el The context element
4850      * @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'})
4851      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4852      * @return {HTMLElement/Roo.Element} The new node or Element
4853      */
4854     append : function(el, values, returnElement){
4855         return this.doInsert('beforeEnd', el, values, returnElement);
4856     },
4857
4858     doInsert : function(where, el, values, returnEl){
4859         el = Roo.getDom(el);
4860         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4861         return returnEl ? Roo.get(newNode, true) : newNode;
4862     },
4863
4864     /**
4865      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4866      * @param {String/HTMLElement/Roo.Element} el The context element
4867      * @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'})
4868      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4869      * @return {HTMLElement/Roo.Element} The new node or Element
4870      */
4871     overwrite : function(el, values, returnElement){
4872         el = Roo.getDom(el);
4873         el.innerHTML = this.applyTemplate(values);
4874         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4875     }
4876 };
4877 /**
4878  * Alias for {@link #applyTemplate}
4879  * @method
4880  */
4881 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4882
4883 // backwards compat
4884 Roo.DomHelper.Template = Roo.Template;
4885
4886 /**
4887  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4888  * @param {String/HTMLElement} el A DOM element or its id
4889  * @returns {Roo.Template} The created template
4890  * @static
4891  */
4892 Roo.Template.from = function(el){
4893     el = Roo.getDom(el);
4894     return new Roo.Template(el.value || el.innerHTML);
4895 };/*
4896  * Based on:
4897  * Ext JS Library 1.1.1
4898  * Copyright(c) 2006-2007, Ext JS, LLC.
4899  *
4900  * Originally Released Under LGPL - original licence link has changed is not relivant.
4901  *
4902  * Fork - LGPL
4903  * <script type="text/javascript">
4904  */
4905  
4906
4907 /*
4908  * This is code is also distributed under MIT license for use
4909  * with jQuery and prototype JavaScript libraries.
4910  */
4911 /**
4912  * @class Roo.DomQuery
4913 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).
4914 <p>
4915 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>
4916
4917 <p>
4918 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.
4919 </p>
4920 <h4>Element Selectors:</h4>
4921 <ul class="list">
4922     <li> <b>*</b> any element</li>
4923     <li> <b>E</b> an element with the tag E</li>
4924     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4925     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4926     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4927     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4928 </ul>
4929 <h4>Attribute Selectors:</h4>
4930 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4931 <ul class="list">
4932     <li> <b>E[foo]</b> has an attribute "foo"</li>
4933     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4934     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4935     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4936     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4937     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4938     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4939 </ul>
4940 <h4>Pseudo Classes:</h4>
4941 <ul class="list">
4942     <li> <b>E:first-child</b> E is the first child of its parent</li>
4943     <li> <b>E:last-child</b> E is the last child of its parent</li>
4944     <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>
4945     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4946     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4947     <li> <b>E:only-child</b> E is the only child of its parent</li>
4948     <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>
4949     <li> <b>E:first</b> the first E in the resultset</li>
4950     <li> <b>E:last</b> the last E in the resultset</li>
4951     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4952     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4953     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4954     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4955     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4956     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4957     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4958     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4959     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4960 </ul>
4961 <h4>CSS Value Selectors:</h4>
4962 <ul class="list">
4963     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4964     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4965     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4966     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4967     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4968     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4969 </ul>
4970  * @singleton
4971  */
4972 Roo.DomQuery = function(){
4973     var cache = {}, simpleCache = {}, valueCache = {};
4974     var nonSpace = /\S/;
4975     var trimRe = /^\s+|\s+$/g;
4976     var tplRe = /\{(\d+)\}/g;
4977     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4978     var tagTokenRe = /^(#)?([\w-\*]+)/;
4979     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4980
4981     function child(p, index){
4982         var i = 0;
4983         var n = p.firstChild;
4984         while(n){
4985             if(n.nodeType == 1){
4986                if(++i == index){
4987                    return n;
4988                }
4989             }
4990             n = n.nextSibling;
4991         }
4992         return null;
4993     };
4994
4995     function next(n){
4996         while((n = n.nextSibling) && n.nodeType != 1);
4997         return n;
4998     };
4999
5000     function prev(n){
5001         while((n = n.previousSibling) && n.nodeType != 1);
5002         return n;
5003     };
5004
5005     function children(d){
5006         var n = d.firstChild, ni = -1;
5007             while(n){
5008                 var nx = n.nextSibling;
5009                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5010                     d.removeChild(n);
5011                 }else{
5012                     n.nodeIndex = ++ni;
5013                 }
5014                 n = nx;
5015             }
5016             return this;
5017         };
5018
5019     function byClassName(c, a, v){
5020         if(!v){
5021             return c;
5022         }
5023         var r = [], ri = -1, cn;
5024         for(var i = 0, ci; ci = c[i]; i++){
5025             if((' '+ci.className+' ').indexOf(v) != -1){
5026                 r[++ri] = ci;
5027             }
5028         }
5029         return r;
5030     };
5031
5032     function attrValue(n, attr){
5033         if(!n.tagName && typeof n.length != "undefined"){
5034             n = n[0];
5035         }
5036         if(!n){
5037             return null;
5038         }
5039         if(attr == "for"){
5040             return n.htmlFor;
5041         }
5042         if(attr == "class" || attr == "className"){
5043             return n.className;
5044         }
5045         return n.getAttribute(attr) || n[attr];
5046
5047     };
5048
5049     function getNodes(ns, mode, tagName){
5050         var result = [], ri = -1, cs;
5051         if(!ns){
5052             return result;
5053         }
5054         tagName = tagName || "*";
5055         if(typeof ns.getElementsByTagName != "undefined"){
5056             ns = [ns];
5057         }
5058         if(!mode){
5059             for(var i = 0, ni; ni = ns[i]; i++){
5060                 cs = ni.getElementsByTagName(tagName);
5061                 for(var j = 0, ci; ci = cs[j]; j++){
5062                     result[++ri] = ci;
5063                 }
5064             }
5065         }else if(mode == "/" || mode == ">"){
5066             var utag = tagName.toUpperCase();
5067             for(var i = 0, ni, cn; ni = ns[i]; i++){
5068                 cn = ni.children || ni.childNodes;
5069                 for(var j = 0, cj; cj = cn[j]; j++){
5070                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5071                         result[++ri] = cj;
5072                     }
5073                 }
5074             }
5075         }else if(mode == "+"){
5076             var utag = tagName.toUpperCase();
5077             for(var i = 0, n; n = ns[i]; i++){
5078                 while((n = n.nextSibling) && n.nodeType != 1);
5079                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5080                     result[++ri] = n;
5081                 }
5082             }
5083         }else if(mode == "~"){
5084             for(var i = 0, n; n = ns[i]; i++){
5085                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5086                 if(n){
5087                     result[++ri] = n;
5088                 }
5089             }
5090         }
5091         return result;
5092     };
5093
5094     function concat(a, b){
5095         if(b.slice){
5096             return a.concat(b);
5097         }
5098         for(var i = 0, l = b.length; i < l; i++){
5099             a[a.length] = b[i];
5100         }
5101         return a;
5102     }
5103
5104     function byTag(cs, tagName){
5105         if(cs.tagName || cs == document){
5106             cs = [cs];
5107         }
5108         if(!tagName){
5109             return cs;
5110         }
5111         var r = [], ri = -1;
5112         tagName = tagName.toLowerCase();
5113         for(var i = 0, ci; ci = cs[i]; i++){
5114             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5115                 r[++ri] = ci;
5116             }
5117         }
5118         return r;
5119     };
5120
5121     function byId(cs, attr, id){
5122         if(cs.tagName || cs == document){
5123             cs = [cs];
5124         }
5125         if(!id){
5126             return cs;
5127         }
5128         var r = [], ri = -1;
5129         for(var i = 0,ci; ci = cs[i]; i++){
5130             if(ci && ci.id == id){
5131                 r[++ri] = ci;
5132                 return r;
5133             }
5134         }
5135         return r;
5136     };
5137
5138     function byAttribute(cs, attr, value, op, custom){
5139         var r = [], ri = -1, st = custom=="{";
5140         var f = Roo.DomQuery.operators[op];
5141         for(var i = 0, ci; ci = cs[i]; i++){
5142             var a;
5143             if(st){
5144                 a = Roo.DomQuery.getStyle(ci, attr);
5145             }
5146             else if(attr == "class" || attr == "className"){
5147                 a = ci.className;
5148             }else if(attr == "for"){
5149                 a = ci.htmlFor;
5150             }else if(attr == "href"){
5151                 a = ci.getAttribute("href", 2);
5152             }else{
5153                 a = ci.getAttribute(attr);
5154             }
5155             if((f && f(a, value)) || (!f && a)){
5156                 r[++ri] = ci;
5157             }
5158         }
5159         return r;
5160     };
5161
5162     function byPseudo(cs, name, value){
5163         return Roo.DomQuery.pseudos[name](cs, value);
5164     };
5165
5166     // This is for IE MSXML which does not support expandos.
5167     // IE runs the same speed using setAttribute, however FF slows way down
5168     // and Safari completely fails so they need to continue to use expandos.
5169     var isIE = window.ActiveXObject ? true : false;
5170
5171     // this eval is stop the compressor from
5172     // renaming the variable to something shorter
5173     
5174     /** eval:var:batch */
5175     var batch = 30803; 
5176
5177     var key = 30803;
5178
5179     function nodupIEXml(cs){
5180         var d = ++key;
5181         cs[0].setAttribute("_nodup", d);
5182         var r = [cs[0]];
5183         for(var i = 1, len = cs.length; i < len; i++){
5184             var c = cs[i];
5185             if(!c.getAttribute("_nodup") != d){
5186                 c.setAttribute("_nodup", d);
5187                 r[r.length] = c;
5188             }
5189         }
5190         for(var i = 0, len = cs.length; i < len; i++){
5191             cs[i].removeAttribute("_nodup");
5192         }
5193         return r;
5194     }
5195
5196     function nodup(cs){
5197         if(!cs){
5198             return [];
5199         }
5200         var len = cs.length, c, i, r = cs, cj, ri = -1;
5201         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5202             return cs;
5203         }
5204         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5205             return nodupIEXml(cs);
5206         }
5207         var d = ++key;
5208         cs[0]._nodup = d;
5209         for(i = 1; c = cs[i]; i++){
5210             if(c._nodup != d){
5211                 c._nodup = d;
5212             }else{
5213                 r = [];
5214                 for(var j = 0; j < i; j++){
5215                     r[++ri] = cs[j];
5216                 }
5217                 for(j = i+1; cj = cs[j]; j++){
5218                     if(cj._nodup != d){
5219                         cj._nodup = d;
5220                         r[++ri] = cj;
5221                     }
5222                 }
5223                 return r;
5224             }
5225         }
5226         return r;
5227     }
5228
5229     function quickDiffIEXml(c1, c2){
5230         var d = ++key;
5231         for(var i = 0, len = c1.length; i < len; i++){
5232             c1[i].setAttribute("_qdiff", d);
5233         }
5234         var r = [];
5235         for(var i = 0, len = c2.length; i < len; i++){
5236             if(c2[i].getAttribute("_qdiff") != d){
5237                 r[r.length] = c2[i];
5238             }
5239         }
5240         for(var i = 0, len = c1.length; i < len; i++){
5241            c1[i].removeAttribute("_qdiff");
5242         }
5243         return r;
5244     }
5245
5246     function quickDiff(c1, c2){
5247         var len1 = c1.length;
5248         if(!len1){
5249             return c2;
5250         }
5251         if(isIE && c1[0].selectSingleNode){
5252             return quickDiffIEXml(c1, c2);
5253         }
5254         var d = ++key;
5255         for(var i = 0; i < len1; i++){
5256             c1[i]._qdiff = d;
5257         }
5258         var r = [];
5259         for(var i = 0, len = c2.length; i < len; i++){
5260             if(c2[i]._qdiff != d){
5261                 r[r.length] = c2[i];
5262             }
5263         }
5264         return r;
5265     }
5266
5267     function quickId(ns, mode, root, id){
5268         if(ns == root){
5269            var d = root.ownerDocument || root;
5270            return d.getElementById(id);
5271         }
5272         ns = getNodes(ns, mode, "*");
5273         return byId(ns, null, id);
5274     }
5275
5276     return {
5277         getStyle : function(el, name){
5278             return Roo.fly(el).getStyle(name);
5279         },
5280         /**
5281          * Compiles a selector/xpath query into a reusable function. The returned function
5282          * takes one parameter "root" (optional), which is the context node from where the query should start.
5283          * @param {String} selector The selector/xpath query
5284          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5285          * @return {Function}
5286          */
5287         compile : function(path, type){
5288             type = type || "select";
5289             
5290             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5291             var q = path, mode, lq;
5292             var tk = Roo.DomQuery.matchers;
5293             var tklen = tk.length;
5294             var mm;
5295
5296             // accept leading mode switch
5297             var lmode = q.match(modeRe);
5298             if(lmode && lmode[1]){
5299                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5300                 q = q.replace(lmode[1], "");
5301             }
5302             // strip leading slashes
5303             while(path.substr(0, 1)=="/"){
5304                 path = path.substr(1);
5305             }
5306
5307             while(q && lq != q){
5308                 lq = q;
5309                 var tm = q.match(tagTokenRe);
5310                 if(type == "select"){
5311                     if(tm){
5312                         if(tm[1] == "#"){
5313                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5314                         }else{
5315                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5316                         }
5317                         q = q.replace(tm[0], "");
5318                     }else if(q.substr(0, 1) != '@'){
5319                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5320                     }
5321                 }else{
5322                     if(tm){
5323                         if(tm[1] == "#"){
5324                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5325                         }else{
5326                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5327                         }
5328                         q = q.replace(tm[0], "");
5329                     }
5330                 }
5331                 while(!(mm = q.match(modeRe))){
5332                     var matched = false;
5333                     for(var j = 0; j < tklen; j++){
5334                         var t = tk[j];
5335                         var m = q.match(t.re);
5336                         if(m){
5337                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5338                                                     return m[i];
5339                                                 });
5340                             q = q.replace(m[0], "");
5341                             matched = true;
5342                             break;
5343                         }
5344                     }
5345                     // prevent infinite loop on bad selector
5346                     if(!matched){
5347                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5348                     }
5349                 }
5350                 if(mm[1]){
5351                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5352                     q = q.replace(mm[1], "");
5353                 }
5354             }
5355             fn[fn.length] = "return nodup(n);\n}";
5356             
5357              /** 
5358               * list of variables that need from compression as they are used by eval.
5359              *  eval:var:batch 
5360              *  eval:var:nodup
5361              *  eval:var:byTag
5362              *  eval:var:ById
5363              *  eval:var:getNodes
5364              *  eval:var:quickId
5365              *  eval:var:mode
5366              *  eval:var:root
5367              *  eval:var:n
5368              *  eval:var:byClassName
5369              *  eval:var:byPseudo
5370              *  eval:var:byAttribute
5371              *  eval:var:attrValue
5372              * 
5373              **/ 
5374             eval(fn.join(""));
5375             return f;
5376         },
5377
5378         /**
5379          * Selects a group of elements.
5380          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5381          * @param {Node} root (optional) The start of the query (defaults to document).
5382          * @return {Array}
5383          */
5384         select : function(path, root, type){
5385             if(!root || root == document){
5386                 root = document;
5387             }
5388             if(typeof root == "string"){
5389                 root = document.getElementById(root);
5390             }
5391             var paths = path.split(",");
5392             var results = [];
5393             for(var i = 0, len = paths.length; i < len; i++){
5394                 var p = paths[i].replace(trimRe, "");
5395                 if(!cache[p]){
5396                     cache[p] = Roo.DomQuery.compile(p);
5397                     if(!cache[p]){
5398                         throw p + " is not a valid selector";
5399                     }
5400                 }
5401                 var result = cache[p](root);
5402                 if(result && result != document){
5403                     results = results.concat(result);
5404                 }
5405             }
5406             if(paths.length > 1){
5407                 return nodup(results);
5408             }
5409             return results;
5410         },
5411
5412         /**
5413          * Selects a single element.
5414          * @param {String} selector The selector/xpath query
5415          * @param {Node} root (optional) The start of the query (defaults to document).
5416          * @return {Element}
5417          */
5418         selectNode : function(path, root){
5419             return Roo.DomQuery.select(path, root)[0];
5420         },
5421
5422         /**
5423          * Selects the value of a node, optionally replacing null with the defaultValue.
5424          * @param {String} selector The selector/xpath query
5425          * @param {Node} root (optional) The start of the query (defaults to document).
5426          * @param {String} defaultValue
5427          */
5428         selectValue : function(path, root, defaultValue){
5429             path = path.replace(trimRe, "");
5430             if(!valueCache[path]){
5431                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5432             }
5433             var n = valueCache[path](root);
5434             n = n[0] ? n[0] : n;
5435             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5436             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5437         },
5438
5439         /**
5440          * Selects the value of a node, parsing integers and floats.
5441          * @param {String} selector The selector/xpath query
5442          * @param {Node} root (optional) The start of the query (defaults to document).
5443          * @param {Number} defaultValue
5444          * @return {Number}
5445          */
5446         selectNumber : function(path, root, defaultValue){
5447             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5448             return parseFloat(v);
5449         },
5450
5451         /**
5452          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5453          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5454          * @param {String} selector The simple selector to test
5455          * @return {Boolean}
5456          */
5457         is : function(el, ss){
5458             if(typeof el == "string"){
5459                 el = document.getElementById(el);
5460             }
5461             var isArray = (el instanceof Array);
5462             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5463             return isArray ? (result.length == el.length) : (result.length > 0);
5464         },
5465
5466         /**
5467          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5468          * @param {Array} el An array of elements to filter
5469          * @param {String} selector The simple selector to test
5470          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5471          * the selector instead of the ones that match
5472          * @return {Array}
5473          */
5474         filter : function(els, ss, nonMatches){
5475             ss = ss.replace(trimRe, "");
5476             if(!simpleCache[ss]){
5477                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5478             }
5479             var result = simpleCache[ss](els);
5480             return nonMatches ? quickDiff(result, els) : result;
5481         },
5482
5483         /**
5484          * Collection of matching regular expressions and code snippets.
5485          */
5486         matchers : [{
5487                 re: /^\.([\w-]+)/,
5488                 select: 'n = byClassName(n, null, " {1} ");'
5489             }, {
5490                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5491                 select: 'n = byPseudo(n, "{1}", "{2}");'
5492             },{
5493                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5494                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5495             }, {
5496                 re: /^#([\w-]+)/,
5497                 select: 'n = byId(n, null, "{1}");'
5498             },{
5499                 re: /^@([\w-]+)/,
5500                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5501             }
5502         ],
5503
5504         /**
5505          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5506          * 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;.
5507          */
5508         operators : {
5509             "=" : function(a, v){
5510                 return a == v;
5511             },
5512             "!=" : function(a, v){
5513                 return a != v;
5514             },
5515             "^=" : function(a, v){
5516                 return a && a.substr(0, v.length) == v;
5517             },
5518             "$=" : function(a, v){
5519                 return a && a.substr(a.length-v.length) == v;
5520             },
5521             "*=" : function(a, v){
5522                 return a && a.indexOf(v) !== -1;
5523             },
5524             "%=" : function(a, v){
5525                 return (a % v) == 0;
5526             },
5527             "|=" : function(a, v){
5528                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5529             },
5530             "~=" : function(a, v){
5531                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5532             }
5533         },
5534
5535         /**
5536          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5537          * and the argument (if any) supplied in the selector.
5538          */
5539         pseudos : {
5540             "first-child" : function(c){
5541                 var r = [], ri = -1, n;
5542                 for(var i = 0, ci; ci = n = c[i]; i++){
5543                     while((n = n.previousSibling) && n.nodeType != 1);
5544                     if(!n){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "last-child" : function(c){
5552                 var r = [], ri = -1, n;
5553                 for(var i = 0, ci; ci = n = c[i]; i++){
5554                     while((n = n.nextSibling) && n.nodeType != 1);
5555                     if(!n){
5556                         r[++ri] = ci;
5557                     }
5558                 }
5559                 return r;
5560             },
5561
5562             "nth-child" : function(c, a) {
5563                 var r = [], ri = -1;
5564                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5565                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5566                 for(var i = 0, n; n = c[i]; i++){
5567                     var pn = n.parentNode;
5568                     if (batch != pn._batch) {
5569                         var j = 0;
5570                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5571                             if(cn.nodeType == 1){
5572                                cn.nodeIndex = ++j;
5573                             }
5574                         }
5575                         pn._batch = batch;
5576                     }
5577                     if (f == 1) {
5578                         if (l == 0 || n.nodeIndex == l){
5579                             r[++ri] = n;
5580                         }
5581                     } else if ((n.nodeIndex + l) % f == 0){
5582                         r[++ri] = n;
5583                     }
5584                 }
5585
5586                 return r;
5587             },
5588
5589             "only-child" : function(c){
5590                 var r = [], ri = -1;;
5591                 for(var i = 0, ci; ci = c[i]; i++){
5592                     if(!prev(ci) && !next(ci)){
5593                         r[++ri] = ci;
5594                     }
5595                 }
5596                 return r;
5597             },
5598
5599             "empty" : function(c){
5600                 var r = [], ri = -1;
5601                 for(var i = 0, ci; ci = c[i]; i++){
5602                     var cns = ci.childNodes, j = 0, cn, empty = true;
5603                     while(cn = cns[j]){
5604                         ++j;
5605                         if(cn.nodeType == 1 || cn.nodeType == 3){
5606                             empty = false;
5607                             break;
5608                         }
5609                     }
5610                     if(empty){
5611                         r[++ri] = ci;
5612                     }
5613                 }
5614                 return r;
5615             },
5616
5617             "contains" : function(c, v){
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5621                         r[++ri] = ci;
5622                     }
5623                 }
5624                 return r;
5625             },
5626
5627             "nodeValue" : function(c, v){
5628                 var r = [], ri = -1;
5629                 for(var i = 0, ci; ci = c[i]; i++){
5630                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5631                         r[++ri] = ci;
5632                     }
5633                 }
5634                 return r;
5635             },
5636
5637             "checked" : function(c){
5638                 var r = [], ri = -1;
5639                 for(var i = 0, ci; ci = c[i]; i++){
5640                     if(ci.checked == true){
5641                         r[++ri] = ci;
5642                     }
5643                 }
5644                 return r;
5645             },
5646
5647             "not" : function(c, ss){
5648                 return Roo.DomQuery.filter(c, ss, true);
5649             },
5650
5651             "odd" : function(c){
5652                 return this["nth-child"](c, "odd");
5653             },
5654
5655             "even" : function(c){
5656                 return this["nth-child"](c, "even");
5657             },
5658
5659             "nth" : function(c, a){
5660                 return c[a-1] || [];
5661             },
5662
5663             "first" : function(c){
5664                 return c[0] || [];
5665             },
5666
5667             "last" : function(c){
5668                 return c[c.length-1] || [];
5669             },
5670
5671             "has" : function(c, ss){
5672                 var s = Roo.DomQuery.select;
5673                 var r = [], ri = -1;
5674                 for(var i = 0, ci; ci = c[i]; i++){
5675                     if(s(ss, ci).length > 0){
5676                         r[++ri] = ci;
5677                     }
5678                 }
5679                 return r;
5680             },
5681
5682             "next" : function(c, ss){
5683                 var is = Roo.DomQuery.is;
5684                 var r = [], ri = -1;
5685                 for(var i = 0, ci; ci = c[i]; i++){
5686                     var n = next(ci);
5687                     if(n && is(n, ss)){
5688                         r[++ri] = ci;
5689                     }
5690                 }
5691                 return r;
5692             },
5693
5694             "prev" : function(c, ss){
5695                 var is = Roo.DomQuery.is;
5696                 var r = [], ri = -1;
5697                 for(var i = 0, ci; ci = c[i]; i++){
5698                     var n = prev(ci);
5699                     if(n && is(n, ss)){
5700                         r[++ri] = ci;
5701                     }
5702                 }
5703                 return r;
5704             }
5705         }
5706     };
5707 }();
5708
5709 /**
5710  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5711  * @param {String} path The selector/xpath query
5712  * @param {Node} root (optional) The start of the query (defaults to document).
5713  * @return {Array}
5714  * @member Roo
5715  * @method query
5716  */
5717 Roo.query = Roo.DomQuery.select;
5718 /*
5719  * Based on:
5720  * Ext JS Library 1.1.1
5721  * Copyright(c) 2006-2007, Ext JS, LLC.
5722  *
5723  * Originally Released Under LGPL - original licence link has changed is not relivant.
5724  *
5725  * Fork - LGPL
5726  * <script type="text/javascript">
5727  */
5728
5729 /**
5730  * @class Roo.util.Observable
5731  * Base class that provides a common interface for publishing events. Subclasses are expected to
5732  * to have a property "events" with all the events defined.<br>
5733  * For example:
5734  * <pre><code>
5735  Employee = function(name){
5736     this.name = name;
5737     this.addEvents({
5738         "fired" : true,
5739         "quit" : true
5740     });
5741  }
5742  Roo.extend(Employee, Roo.util.Observable);
5743 </code></pre>
5744  * @param {Object} config properties to use (incuding events / listeners)
5745  */
5746
5747 Roo.util.Observable = function(cfg){
5748     
5749     cfg = cfg|| {};
5750     this.addEvents(cfg.events || {});
5751     if (cfg.events) {
5752         delete cfg.events; // make sure
5753     }
5754      
5755     Roo.apply(this, cfg);
5756     
5757     if(this.listeners){
5758         this.on(this.listeners);
5759         delete this.listeners;
5760     }
5761 };
5762 Roo.util.Observable.prototype = {
5763     /** 
5764  * @cfg {Object} listeners  list of events and functions to call for this object, 
5765  * For example :
5766  * <pre><code>
5767     listeners :  { 
5768        'click' : function(e) {
5769            ..... 
5770         } ,
5771         .... 
5772     } 
5773   </code></pre>
5774  */
5775     
5776     
5777     /**
5778      * Fires the specified event with the passed parameters (minus the event name).
5779      * @param {String} eventName
5780      * @param {Object...} args Variable number of parameters are passed to handlers
5781      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5782      */
5783     fireEvent : function(){
5784         var ce = this.events[arguments[0].toLowerCase()];
5785         if(typeof ce == "object"){
5786             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5787         }else{
5788             return true;
5789         }
5790     },
5791
5792     // private
5793     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5794
5795     /**
5796      * Appends an event handler to this component
5797      * @param {String}   eventName The type of event to listen for
5798      * @param {Function} handler The method the event invokes
5799      * @param {Object}   scope (optional) The scope in which to execute the handler
5800      * function. The handler function's "this" context.
5801      * @param {Object}   options (optional) An object containing handler configuration
5802      * properties. This may contain any of the following properties:<ul>
5803      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5804      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5805      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5806      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5807      * by the specified number of milliseconds. If the event fires again within that time, the original
5808      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5809      * </ul><br>
5810      * <p>
5811      * <b>Combining Options</b><br>
5812      * Using the options argument, it is possible to combine different types of listeners:<br>
5813      * <br>
5814      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5815                 <pre><code>
5816                 el.on('click', this.onClick, this, {
5817                         single: true,
5818                 delay: 100,
5819                 forumId: 4
5820                 });
5821                 </code></pre>
5822      * <p>
5823      * <b>Attaching multiple handlers in 1 call</b><br>
5824      * The method also allows for a single argument to be passed which is a config object containing properties
5825      * which specify multiple handlers.
5826      * <pre><code>
5827                 el.on({
5828                         'click': {
5829                         fn: this.onClick,
5830                         scope: this,
5831                         delay: 100
5832                 }, 
5833                 'mouseover': {
5834                         fn: this.onMouseOver,
5835                         scope: this
5836                 },
5837                 'mouseout': {
5838                         fn: this.onMouseOut,
5839                         scope: this
5840                 }
5841                 });
5842                 </code></pre>
5843      * <p>
5844      * Or a shorthand syntax which passes the same scope object to all handlers:
5845         <pre><code>
5846                 el.on({
5847                         'click': this.onClick,
5848                 'mouseover': this.onMouseOver,
5849                 'mouseout': this.onMouseOut,
5850                 scope: this
5851                 });
5852                 </code></pre>
5853      */
5854     addListener : function(eventName, fn, scope, o){
5855         if(typeof eventName == "object"){
5856             o = eventName;
5857             for(var e in o){
5858                 if(this.filterOptRe.test(e)){
5859                     continue;
5860                 }
5861                 if(typeof o[e] == "function"){
5862                     // shared options
5863                     this.addListener(e, o[e], o.scope,  o);
5864                 }else{
5865                     // individual options
5866                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5867                 }
5868             }
5869             return;
5870         }
5871         o = (!o || typeof o == "boolean") ? {} : o;
5872         eventName = eventName.toLowerCase();
5873         var ce = this.events[eventName] || true;
5874         if(typeof ce == "boolean"){
5875             ce = new Roo.util.Event(this, eventName);
5876             this.events[eventName] = ce;
5877         }
5878         ce.addListener(fn, scope, o);
5879     },
5880
5881     /**
5882      * Removes a listener
5883      * @param {String}   eventName     The type of event to listen for
5884      * @param {Function} handler        The handler to remove
5885      * @param {Object}   scope  (optional) The scope (this object) for the handler
5886      */
5887     removeListener : function(eventName, fn, scope){
5888         var ce = this.events[eventName.toLowerCase()];
5889         if(typeof ce == "object"){
5890             ce.removeListener(fn, scope);
5891         }
5892     },
5893
5894     /**
5895      * Removes all listeners for this object
5896      */
5897     purgeListeners : function(){
5898         for(var evt in this.events){
5899             if(typeof this.events[evt] == "object"){
5900                  this.events[evt].clearListeners();
5901             }
5902         }
5903     },
5904
5905     relayEvents : function(o, events){
5906         var createHandler = function(ename){
5907             return function(){
5908                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5909             };
5910         };
5911         for(var i = 0, len = events.length; i < len; i++){
5912             var ename = events[i];
5913             if(!this.events[ename]){ this.events[ename] = true; };
5914             o.on(ename, createHandler(ename), this);
5915         }
5916     },
5917
5918     /**
5919      * Used to define events on this Observable
5920      * @param {Object} object The object with the events defined
5921      */
5922     addEvents : function(o){
5923         if(!this.events){
5924             this.events = {};
5925         }
5926         Roo.applyIf(this.events, o);
5927     },
5928
5929     /**
5930      * Checks to see if this object has any listeners for a specified event
5931      * @param {String} eventName The name of the event to check for
5932      * @return {Boolean} True if the event is being listened for, else false
5933      */
5934     hasListener : function(eventName){
5935         var e = this.events[eventName];
5936         return typeof e == "object" && e.listeners.length > 0;
5937     }
5938 };
5939 /**
5940  * Appends an event handler to this element (shorthand for addListener)
5941  * @param {String}   eventName     The type of event to listen for
5942  * @param {Function} handler        The method the event invokes
5943  * @param {Object}   scope (optional) The scope in which to execute the handler
5944  * function. The handler function's "this" context.
5945  * @param {Object}   options  (optional)
5946  * @method
5947  */
5948 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5949 /**
5950  * Removes a listener (shorthand for removeListener)
5951  * @param {String}   eventName     The type of event to listen for
5952  * @param {Function} handler        The handler to remove
5953  * @param {Object}   scope  (optional) The scope (this object) for the handler
5954  * @method
5955  */
5956 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5957
5958 /**
5959  * Starts capture on the specified Observable. All events will be passed
5960  * to the supplied function with the event name + standard signature of the event
5961  * <b>before</b> the event is fired. If the supplied function returns false,
5962  * the event will not fire.
5963  * @param {Observable} o The Observable to capture
5964  * @param {Function} fn The function to call
5965  * @param {Object} scope (optional) The scope (this object) for the fn
5966  * @static
5967  */
5968 Roo.util.Observable.capture = function(o, fn, scope){
5969     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5970 };
5971
5972 /**
5973  * Removes <b>all</b> added captures from the Observable.
5974  * @param {Observable} o The Observable to release
5975  * @static
5976  */
5977 Roo.util.Observable.releaseCapture = function(o){
5978     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5979 };
5980
5981 (function(){
5982
5983     var createBuffered = function(h, o, scope){
5984         var task = new Roo.util.DelayedTask();
5985         return function(){
5986             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5987         };
5988     };
5989
5990     var createSingle = function(h, e, fn, scope){
5991         return function(){
5992             e.removeListener(fn, scope);
5993             return h.apply(scope, arguments);
5994         };
5995     };
5996
5997     var createDelayed = function(h, o, scope){
5998         return function(){
5999             var args = Array.prototype.slice.call(arguments, 0);
6000             setTimeout(function(){
6001                 h.apply(scope, args);
6002             }, o.delay || 10);
6003         };
6004     };
6005
6006     Roo.util.Event = function(obj, name){
6007         this.name = name;
6008         this.obj = obj;
6009         this.listeners = [];
6010     };
6011
6012     Roo.util.Event.prototype = {
6013         addListener : function(fn, scope, options){
6014             var o = options || {};
6015             scope = scope || this.obj;
6016             if(!this.isListening(fn, scope)){
6017                 var l = {fn: fn, scope: scope, options: o};
6018                 var h = fn;
6019                 if(o.delay){
6020                     h = createDelayed(h, o, scope);
6021                 }
6022                 if(o.single){
6023                     h = createSingle(h, this, fn, scope);
6024                 }
6025                 if(o.buffer){
6026                     h = createBuffered(h, o, scope);
6027                 }
6028                 l.fireFn = h;
6029                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6030                     this.listeners.push(l);
6031                 }else{
6032                     this.listeners = this.listeners.slice(0);
6033                     this.listeners.push(l);
6034                 }
6035             }
6036         },
6037
6038         findListener : function(fn, scope){
6039             scope = scope || this.obj;
6040             var ls = this.listeners;
6041             for(var i = 0, len = ls.length; i < len; i++){
6042                 var l = ls[i];
6043                 if(l.fn == fn && l.scope == scope){
6044                     return i;
6045                 }
6046             }
6047             return -1;
6048         },
6049
6050         isListening : function(fn, scope){
6051             return this.findListener(fn, scope) != -1;
6052         },
6053
6054         removeListener : function(fn, scope){
6055             var index;
6056             if((index = this.findListener(fn, scope)) != -1){
6057                 if(!this.firing){
6058                     this.listeners.splice(index, 1);
6059                 }else{
6060                     this.listeners = this.listeners.slice(0);
6061                     this.listeners.splice(index, 1);
6062                 }
6063                 return true;
6064             }
6065             return false;
6066         },
6067
6068         clearListeners : function(){
6069             this.listeners = [];
6070         },
6071
6072         fire : function(){
6073             var ls = this.listeners, scope, len = ls.length;
6074             if(len > 0){
6075                 this.firing = true;
6076                 var args = Array.prototype.slice.call(arguments, 0);
6077                 for(var i = 0; i < len; i++){
6078                     var l = ls[i];
6079                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6080                         this.firing = false;
6081                         return false;
6082                     }
6083                 }
6084                 this.firing = false;
6085             }
6086             return true;
6087         }
6088     };
6089 })();/*
6090  * RooJS Library 
6091  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6092  *
6093  * Licence LGPL 
6094  *
6095  */
6096  
6097 /**
6098  * @class Roo.Document
6099  * @extends Roo.util.Observable
6100  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6101  * 
6102  * @param {Object} config the methods and properties of the 'base' class for the application.
6103  * 
6104  *  Generic Page handler - implement this to start your app..
6105  * 
6106  * eg.
6107  *  MyProject = new Roo.Document({
6108         events : {
6109             'load' : true // your events..
6110         },
6111         listeners : {
6112             'ready' : function() {
6113                 // fired on Roo.onReady()
6114             }
6115         }
6116  * 
6117  */
6118 Roo.Document = function(cfg) {
6119      
6120     this.addEvents({ 
6121         'ready' : true
6122     });
6123     Roo.util.Observable.call(this,cfg);
6124     
6125     var _this = this;
6126     
6127     Roo.onReady(function() {
6128         _this.fireEvent('ready');
6129     },null,false);
6130     
6131     
6132 }
6133
6134 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6135  * Based on:
6136  * Ext JS Library 1.1.1
6137  * Copyright(c) 2006-2007, Ext JS, LLC.
6138  *
6139  * Originally Released Under LGPL - original licence link has changed is not relivant.
6140  *
6141  * Fork - LGPL
6142  * <script type="text/javascript">
6143  */
6144
6145 /**
6146  * @class Roo.EventManager
6147  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6148  * several useful events directly.
6149  * See {@link Roo.EventObject} for more details on normalized event objects.
6150  * @singleton
6151  */
6152 Roo.EventManager = function(){
6153     var docReadyEvent, docReadyProcId, docReadyState = false;
6154     var resizeEvent, resizeTask, textEvent, textSize;
6155     var E = Roo.lib.Event;
6156     var D = Roo.lib.Dom;
6157
6158     
6159     
6160
6161     var fireDocReady = function(){
6162         if(!docReadyState){
6163             docReadyState = true;
6164             Roo.isReady = true;
6165             if(docReadyProcId){
6166                 clearInterval(docReadyProcId);
6167             }
6168             if(Roo.isGecko || Roo.isOpera) {
6169                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6170             }
6171             if(Roo.isIE){
6172                 var defer = document.getElementById("ie-deferred-loader");
6173                 if(defer){
6174                     defer.onreadystatechange = null;
6175                     defer.parentNode.removeChild(defer);
6176                 }
6177             }
6178             if(docReadyEvent){
6179                 docReadyEvent.fire();
6180                 docReadyEvent.clearListeners();
6181             }
6182         }
6183     };
6184     
6185     var initDocReady = function(){
6186         docReadyEvent = new Roo.util.Event();
6187         if(Roo.isGecko || Roo.isOpera) {
6188             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6189         }else if(Roo.isIE){
6190             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6191             var defer = document.getElementById("ie-deferred-loader");
6192             defer.onreadystatechange = function(){
6193                 if(this.readyState == "complete"){
6194                     fireDocReady();
6195                 }
6196             };
6197         }else if(Roo.isSafari){ 
6198             docReadyProcId = setInterval(function(){
6199                 var rs = document.readyState;
6200                 if(rs == "complete") {
6201                     fireDocReady();     
6202                  }
6203             }, 10);
6204         }
6205         // no matter what, make sure it fires on load
6206         E.on(window, "load", fireDocReady);
6207     };
6208
6209     var createBuffered = function(h, o){
6210         var task = new Roo.util.DelayedTask(h);
6211         return function(e){
6212             // create new event object impl so new events don't wipe out properties
6213             e = new Roo.EventObjectImpl(e);
6214             task.delay(o.buffer, h, null, [e]);
6215         };
6216     };
6217
6218     var createSingle = function(h, el, ename, fn){
6219         return function(e){
6220             Roo.EventManager.removeListener(el, ename, fn);
6221             h(e);
6222         };
6223     };
6224
6225     var createDelayed = function(h, o){
6226         return function(e){
6227             // create new event object impl so new events don't wipe out properties
6228             e = new Roo.EventObjectImpl(e);
6229             setTimeout(function(){
6230                 h(e);
6231             }, o.delay || 10);
6232         };
6233     };
6234     var transitionEndVal = false;
6235     
6236     var transitionEnd = function()
6237     {
6238         if (transitionEndVal) {
6239             return transitionEndVal;
6240         }
6241         var el = document.createElement('div');
6242
6243         var transEndEventNames = {
6244             WebkitTransition : 'webkitTransitionEnd',
6245             MozTransition    : 'transitionend',
6246             OTransition      : 'oTransitionEnd otransitionend',
6247             transition       : 'transitionend'
6248         };
6249     
6250         for (var name in transEndEventNames) {
6251             if (el.style[name] !== undefined) {
6252                 transitionEndVal = transEndEventNames[name];
6253                 return  transitionEndVal ;
6254             }
6255         }
6256     }
6257     
6258
6259     var listen = function(element, ename, opt, fn, scope){
6260         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6261         fn = fn || o.fn; scope = scope || o.scope;
6262         var el = Roo.getDom(element);
6263         
6264         
6265         if(!el){
6266             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6267         }
6268         
6269         if (ename == 'transitionend') {
6270             ename = transitionEnd();
6271         }
6272         var h = function(e){
6273             e = Roo.EventObject.setEvent(e);
6274             var t;
6275             if(o.delegate){
6276                 t = e.getTarget(o.delegate, el);
6277                 if(!t){
6278                     return;
6279                 }
6280             }else{
6281                 t = e.target;
6282             }
6283             if(o.stopEvent === true){
6284                 e.stopEvent();
6285             }
6286             if(o.preventDefault === true){
6287                e.preventDefault();
6288             }
6289             if(o.stopPropagation === true){
6290                 e.stopPropagation();
6291             }
6292
6293             if(o.normalized === false){
6294                 e = e.browserEvent;
6295             }
6296
6297             fn.call(scope || el, e, t, o);
6298         };
6299         if(o.delay){
6300             h = createDelayed(h, o);
6301         }
6302         if(o.single){
6303             h = createSingle(h, el, ename, fn);
6304         }
6305         if(o.buffer){
6306             h = createBuffered(h, o);
6307         }
6308         
6309         fn._handlers = fn._handlers || [];
6310         
6311         
6312         fn._handlers.push([Roo.id(el), ename, h]);
6313         
6314         
6315          
6316         E.on(el, ename, h);
6317         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6318             el.addEventListener("DOMMouseScroll", h, false);
6319             E.on(window, 'unload', function(){
6320                 el.removeEventListener("DOMMouseScroll", h, false);
6321             });
6322         }
6323         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6324             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6325         }
6326         return h;
6327     };
6328
6329     var stopListening = function(el, ename, fn){
6330         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6331         if(hds){
6332             for(var i = 0, len = hds.length; i < len; i++){
6333                 var h = hds[i];
6334                 if(h[0] == id && h[1] == ename){
6335                     hd = h[2];
6336                     hds.splice(i, 1);
6337                     break;
6338                 }
6339             }
6340         }
6341         E.un(el, ename, hd);
6342         el = Roo.getDom(el);
6343         if(ename == "mousewheel" && el.addEventListener){
6344             el.removeEventListener("DOMMouseScroll", hd, false);
6345         }
6346         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6347             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6348         }
6349     };
6350
6351     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6352     
6353     var pub = {
6354         
6355         
6356         /** 
6357          * Fix for doc tools
6358          * @scope Roo.EventManager
6359          */
6360         
6361         
6362         /** 
6363          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6364          * object with a Roo.EventObject
6365          * @param {Function} fn        The method the event invokes
6366          * @param {Object}   scope    An object that becomes the scope of the handler
6367          * @param {boolean}  override If true, the obj passed in becomes
6368          *                             the execution scope of the listener
6369          * @return {Function} The wrapped function
6370          * @deprecated
6371          */
6372         wrap : function(fn, scope, override){
6373             return function(e){
6374                 Roo.EventObject.setEvent(e);
6375                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6376             };
6377         },
6378         
6379         /**
6380      * Appends an event handler to an element (shorthand for addListener)
6381      * @param {String/HTMLElement}   element        The html element or id to assign the
6382      * @param {String}   eventName The type of event to listen for
6383      * @param {Function} handler The method the event invokes
6384      * @param {Object}   scope (optional) The scope in which to execute the handler
6385      * function. The handler function's "this" context.
6386      * @param {Object}   options (optional) An object containing handler configuration
6387      * properties. This may contain any of the following properties:<ul>
6388      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6389      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6390      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6391      * <li>preventDefault {Boolean} True to prevent the default action</li>
6392      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6393      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6394      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6395      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6396      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6397      * by the specified number of milliseconds. If the event fires again within that time, the original
6398      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6399      * </ul><br>
6400      * <p>
6401      * <b>Combining Options</b><br>
6402      * Using the options argument, it is possible to combine different types of listeners:<br>
6403      * <br>
6404      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6405      * Code:<pre><code>
6406 el.on('click', this.onClick, this, {
6407     single: true,
6408     delay: 100,
6409     stopEvent : true,
6410     forumId: 4
6411 });</code></pre>
6412      * <p>
6413      * <b>Attaching multiple handlers in 1 call</b><br>
6414       * The method also allows for a single argument to be passed which is a config object containing properties
6415      * which specify multiple handlers.
6416      * <p>
6417      * Code:<pre><code>
6418 el.on({
6419     'click' : {
6420         fn: this.onClick
6421         scope: this,
6422         delay: 100
6423     },
6424     'mouseover' : {
6425         fn: this.onMouseOver
6426         scope: this
6427     },
6428     'mouseout' : {
6429         fn: this.onMouseOut
6430         scope: this
6431     }
6432 });</code></pre>
6433      * <p>
6434      * Or a shorthand syntax:<br>
6435      * Code:<pre><code>
6436 el.on({
6437     'click' : this.onClick,
6438     'mouseover' : this.onMouseOver,
6439     'mouseout' : this.onMouseOut
6440     scope: this
6441 });</code></pre>
6442      */
6443         addListener : function(element, eventName, fn, scope, options){
6444             if(typeof eventName == "object"){
6445                 var o = eventName;
6446                 for(var e in o){
6447                     if(propRe.test(e)){
6448                         continue;
6449                     }
6450                     if(typeof o[e] == "function"){
6451                         // shared options
6452                         listen(element, e, o, o[e], o.scope);
6453                     }else{
6454                         // individual options
6455                         listen(element, e, o[e]);
6456                     }
6457                 }
6458                 return;
6459             }
6460             return listen(element, eventName, options, fn, scope);
6461         },
6462         
6463         /**
6464          * Removes an event handler
6465          *
6466          * @param {String/HTMLElement}   element        The id or html element to remove the 
6467          *                             event from
6468          * @param {String}   eventName     The type of event
6469          * @param {Function} fn
6470          * @return {Boolean} True if a listener was actually removed
6471          */
6472         removeListener : function(element, eventName, fn){
6473             return stopListening(element, eventName, fn);
6474         },
6475         
6476         /**
6477          * Fires when the document is ready (before onload and before images are loaded). Can be 
6478          * accessed shorthanded Roo.onReady().
6479          * @param {Function} fn        The method the event invokes
6480          * @param {Object}   scope    An  object that becomes the scope of the handler
6481          * @param {boolean}  options
6482          */
6483         onDocumentReady : function(fn, scope, options){
6484             if(docReadyState){ // if it already fired
6485                 docReadyEvent.addListener(fn, scope, options);
6486                 docReadyEvent.fire();
6487                 docReadyEvent.clearListeners();
6488                 return;
6489             }
6490             if(!docReadyEvent){
6491                 initDocReady();
6492             }
6493             docReadyEvent.addListener(fn, scope, options);
6494         },
6495         
6496         /**
6497          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6498          * @param {Function} fn        The method the event invokes
6499          * @param {Object}   scope    An object that becomes the scope of the handler
6500          * @param {boolean}  options
6501          */
6502         onWindowResize : function(fn, scope, options){
6503             if(!resizeEvent){
6504                 resizeEvent = new Roo.util.Event();
6505                 resizeTask = new Roo.util.DelayedTask(function(){
6506                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6507                 });
6508                 E.on(window, "resize", function(){
6509                     if(Roo.isIE){
6510                         resizeTask.delay(50);
6511                     }else{
6512                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6513                     }
6514                 });
6515             }
6516             resizeEvent.addListener(fn, scope, options);
6517         },
6518
6519         /**
6520          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6521          * @param {Function} fn        The method the event invokes
6522          * @param {Object}   scope    An object that becomes the scope of the handler
6523          * @param {boolean}  options
6524          */
6525         onTextResize : function(fn, scope, options){
6526             if(!textEvent){
6527                 textEvent = new Roo.util.Event();
6528                 var textEl = new Roo.Element(document.createElement('div'));
6529                 textEl.dom.className = 'x-text-resize';
6530                 textEl.dom.innerHTML = 'X';
6531                 textEl.appendTo(document.body);
6532                 textSize = textEl.dom.offsetHeight;
6533                 setInterval(function(){
6534                     if(textEl.dom.offsetHeight != textSize){
6535                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6536                     }
6537                 }, this.textResizeInterval);
6538             }
6539             textEvent.addListener(fn, scope, options);
6540         },
6541
6542         /**
6543          * Removes the passed window resize listener.
6544          * @param {Function} fn        The method the event invokes
6545          * @param {Object}   scope    The scope of handler
6546          */
6547         removeResizeListener : function(fn, scope){
6548             if(resizeEvent){
6549                 resizeEvent.removeListener(fn, scope);
6550             }
6551         },
6552
6553         // private
6554         fireResize : function(){
6555             if(resizeEvent){
6556                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6557             }   
6558         },
6559         /**
6560          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6561          */
6562         ieDeferSrc : false,
6563         /**
6564          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6565          */
6566         textResizeInterval : 50
6567     };
6568     
6569     /**
6570      * Fix for doc tools
6571      * @scopeAlias pub=Roo.EventManager
6572      */
6573     
6574      /**
6575      * Appends an event handler to an element (shorthand for addListener)
6576      * @param {String/HTMLElement}   element        The html element or id to assign the
6577      * @param {String}   eventName The type of event to listen for
6578      * @param {Function} handler The method the event invokes
6579      * @param {Object}   scope (optional) The scope in which to execute the handler
6580      * function. The handler function's "this" context.
6581      * @param {Object}   options (optional) An object containing handler configuration
6582      * properties. This may contain any of the following properties:<ul>
6583      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6584      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6585      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6586      * <li>preventDefault {Boolean} True to prevent the default action</li>
6587      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6588      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6589      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6590      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6591      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6592      * by the specified number of milliseconds. If the event fires again within that time, the original
6593      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6594      * </ul><br>
6595      * <p>
6596      * <b>Combining Options</b><br>
6597      * Using the options argument, it is possible to combine different types of listeners:<br>
6598      * <br>
6599      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6600      * Code:<pre><code>
6601 el.on('click', this.onClick, this, {
6602     single: true,
6603     delay: 100,
6604     stopEvent : true,
6605     forumId: 4
6606 });</code></pre>
6607      * <p>
6608      * <b>Attaching multiple handlers in 1 call</b><br>
6609       * The method also allows for a single argument to be passed which is a config object containing properties
6610      * which specify multiple handlers.
6611      * <p>
6612      * Code:<pre><code>
6613 el.on({
6614     'click' : {
6615         fn: this.onClick
6616         scope: this,
6617         delay: 100
6618     },
6619     'mouseover' : {
6620         fn: this.onMouseOver
6621         scope: this
6622     },
6623     'mouseout' : {
6624         fn: this.onMouseOut
6625         scope: this
6626     }
6627 });</code></pre>
6628      * <p>
6629      * Or a shorthand syntax:<br>
6630      * Code:<pre><code>
6631 el.on({
6632     'click' : this.onClick,
6633     'mouseover' : this.onMouseOver,
6634     'mouseout' : this.onMouseOut
6635     scope: this
6636 });</code></pre>
6637      */
6638     pub.on = pub.addListener;
6639     pub.un = pub.removeListener;
6640
6641     pub.stoppedMouseDownEvent = new Roo.util.Event();
6642     return pub;
6643 }();
6644 /**
6645   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6646   * @param {Function} fn        The method the event invokes
6647   * @param {Object}   scope    An  object that becomes the scope of the handler
6648   * @param {boolean}  override If true, the obj passed in becomes
6649   *                             the execution scope of the listener
6650   * @member Roo
6651   * @method onReady
6652  */
6653 Roo.onReady = Roo.EventManager.onDocumentReady;
6654
6655 Roo.onReady(function(){
6656     var bd = Roo.get(document.body);
6657     if(!bd){ return; }
6658
6659     var cls = [
6660             Roo.isIE ? "roo-ie"
6661             : Roo.isIE11 ? "roo-ie11"
6662             : Roo.isEdge ? "roo-edge"
6663             : Roo.isGecko ? "roo-gecko"
6664             : Roo.isOpera ? "roo-opera"
6665             : Roo.isSafari ? "roo-safari" : ""];
6666
6667     if(Roo.isMac){
6668         cls.push("roo-mac");
6669     }
6670     if(Roo.isLinux){
6671         cls.push("roo-linux");
6672     }
6673     if(Roo.isIOS){
6674         cls.push("roo-ios");
6675     }
6676     if(Roo.isTouch){
6677         cls.push("roo-touch");
6678     }
6679     if(Roo.isBorderBox){
6680         cls.push('roo-border-box');
6681     }
6682     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6683         var p = bd.dom.parentNode;
6684         if(p){
6685             p.className += ' roo-strict';
6686         }
6687     }
6688     bd.addClass(cls.join(' '));
6689 });
6690
6691 /**
6692  * @class Roo.EventObject
6693  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6694  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6695  * Example:
6696  * <pre><code>
6697  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6698     e.preventDefault();
6699     var target = e.getTarget();
6700     ...
6701  }
6702  var myDiv = Roo.get("myDiv");
6703  myDiv.on("click", handleClick);
6704  //or
6705  Roo.EventManager.on("myDiv", 'click', handleClick);
6706  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6707  </code></pre>
6708  * @singleton
6709  */
6710 Roo.EventObject = function(){
6711     
6712     var E = Roo.lib.Event;
6713     
6714     // safari keypress events for special keys return bad keycodes
6715     var safariKeys = {
6716         63234 : 37, // left
6717         63235 : 39, // right
6718         63232 : 38, // up
6719         63233 : 40, // down
6720         63276 : 33, // page up
6721         63277 : 34, // page down
6722         63272 : 46, // delete
6723         63273 : 36, // home
6724         63275 : 35  // end
6725     };
6726
6727     // normalize button clicks
6728     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6729                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6730
6731     Roo.EventObjectImpl = function(e){
6732         if(e){
6733             this.setEvent(e.browserEvent || e);
6734         }
6735     };
6736     Roo.EventObjectImpl.prototype = {
6737         /**
6738          * Used to fix doc tools.
6739          * @scope Roo.EventObject.prototype
6740          */
6741             
6742
6743         
6744         
6745         /** The normal browser event */
6746         browserEvent : null,
6747         /** The button pressed in a mouse event */
6748         button : -1,
6749         /** True if the shift key was down during the event */
6750         shiftKey : false,
6751         /** True if the control key was down during the event */
6752         ctrlKey : false,
6753         /** True if the alt key was down during the event */
6754         altKey : false,
6755
6756         /** Key constant 
6757         * @type Number */
6758         BACKSPACE : 8,
6759         /** Key constant 
6760         * @type Number */
6761         TAB : 9,
6762         /** Key constant 
6763         * @type Number */
6764         RETURN : 13,
6765         /** Key constant 
6766         * @type Number */
6767         ENTER : 13,
6768         /** Key constant 
6769         * @type Number */
6770         SHIFT : 16,
6771         /** Key constant 
6772         * @type Number */
6773         CONTROL : 17,
6774         /** Key constant 
6775         * @type Number */
6776         ESC : 27,
6777         /** Key constant 
6778         * @type Number */
6779         SPACE : 32,
6780         /** Key constant 
6781         * @type Number */
6782         PAGEUP : 33,
6783         /** Key constant 
6784         * @type Number */
6785         PAGEDOWN : 34,
6786         /** Key constant 
6787         * @type Number */
6788         END : 35,
6789         /** Key constant 
6790         * @type Number */
6791         HOME : 36,
6792         /** Key constant 
6793         * @type Number */
6794         LEFT : 37,
6795         /** Key constant 
6796         * @type Number */
6797         UP : 38,
6798         /** Key constant 
6799         * @type Number */
6800         RIGHT : 39,
6801         /** Key constant 
6802         * @type Number */
6803         DOWN : 40,
6804         /** Key constant 
6805         * @type Number */
6806         DELETE : 46,
6807         /** Key constant 
6808         * @type Number */
6809         F5 : 116,
6810
6811            /** @private */
6812         setEvent : function(e){
6813             if(e == this || (e && e.browserEvent)){ // already wrapped
6814                 return e;
6815             }
6816             this.browserEvent = e;
6817             if(e){
6818                 // normalize buttons
6819                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6820                 if(e.type == 'click' && this.button == -1){
6821                     this.button = 0;
6822                 }
6823                 this.type = e.type;
6824                 this.shiftKey = e.shiftKey;
6825                 // mac metaKey behaves like ctrlKey
6826                 this.ctrlKey = e.ctrlKey || e.metaKey;
6827                 this.altKey = e.altKey;
6828                 // in getKey these will be normalized for the mac
6829                 this.keyCode = e.keyCode;
6830                 // keyup warnings on firefox.
6831                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6832                 // cache the target for the delayed and or buffered events
6833                 this.target = E.getTarget(e);
6834                 // same for XY
6835                 this.xy = E.getXY(e);
6836             }else{
6837                 this.button = -1;
6838                 this.shiftKey = false;
6839                 this.ctrlKey = false;
6840                 this.altKey = false;
6841                 this.keyCode = 0;
6842                 this.charCode =0;
6843                 this.target = null;
6844                 this.xy = [0, 0];
6845             }
6846             return this;
6847         },
6848
6849         /**
6850          * Stop the event (preventDefault and stopPropagation)
6851          */
6852         stopEvent : function(){
6853             if(this.browserEvent){
6854                 if(this.browserEvent.type == 'mousedown'){
6855                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6856                 }
6857                 E.stopEvent(this.browserEvent);
6858             }
6859         },
6860
6861         /**
6862          * Prevents the browsers default handling of the event.
6863          */
6864         preventDefault : function(){
6865             if(this.browserEvent){
6866                 E.preventDefault(this.browserEvent);
6867             }
6868         },
6869
6870         /** @private */
6871         isNavKeyPress : function(){
6872             var k = this.keyCode;
6873             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6874             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6875         },
6876
6877         isSpecialKey : function(){
6878             var k = this.keyCode;
6879             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6880             (k == 16) || (k == 17) ||
6881             (k >= 18 && k <= 20) ||
6882             (k >= 33 && k <= 35) ||
6883             (k >= 36 && k <= 39) ||
6884             (k >= 44 && k <= 45);
6885         },
6886         /**
6887          * Cancels bubbling of the event.
6888          */
6889         stopPropagation : function(){
6890             if(this.browserEvent){
6891                 if(this.type == 'mousedown'){
6892                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6893                 }
6894                 E.stopPropagation(this.browserEvent);
6895             }
6896         },
6897
6898         /**
6899          * Gets the key code for the event.
6900          * @return {Number}
6901          */
6902         getCharCode : function(){
6903             return this.charCode || this.keyCode;
6904         },
6905
6906         /**
6907          * Returns a normalized keyCode for the event.
6908          * @return {Number} The key code
6909          */
6910         getKey : function(){
6911             var k = this.keyCode || this.charCode;
6912             return Roo.isSafari ? (safariKeys[k] || k) : k;
6913         },
6914
6915         /**
6916          * Gets the x coordinate of the event.
6917          * @return {Number}
6918          */
6919         getPageX : function(){
6920             return this.xy[0];
6921         },
6922
6923         /**
6924          * Gets the y coordinate of the event.
6925          * @return {Number}
6926          */
6927         getPageY : function(){
6928             return this.xy[1];
6929         },
6930
6931         /**
6932          * Gets the time of the event.
6933          * @return {Number}
6934          */
6935         getTime : function(){
6936             if(this.browserEvent){
6937                 return E.getTime(this.browserEvent);
6938             }
6939             return null;
6940         },
6941
6942         /**
6943          * Gets the page coordinates of the event.
6944          * @return {Array} The xy values like [x, y]
6945          */
6946         getXY : function(){
6947             return this.xy;
6948         },
6949
6950         /**
6951          * Gets the target for the event.
6952          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6953          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6954                 search as a number or element (defaults to 10 || document.body)
6955          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6956          * @return {HTMLelement}
6957          */
6958         getTarget : function(selector, maxDepth, returnEl){
6959             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6960         },
6961         /**
6962          * Gets the related target.
6963          * @return {HTMLElement}
6964          */
6965         getRelatedTarget : function(){
6966             if(this.browserEvent){
6967                 return E.getRelatedTarget(this.browserEvent);
6968             }
6969             return null;
6970         },
6971
6972         /**
6973          * Normalizes mouse wheel delta across browsers
6974          * @return {Number} The delta
6975          */
6976         getWheelDelta : function(){
6977             var e = this.browserEvent;
6978             var delta = 0;
6979             if(e.wheelDelta){ /* IE/Opera. */
6980                 delta = e.wheelDelta/120;
6981             }else if(e.detail){ /* Mozilla case. */
6982                 delta = -e.detail/3;
6983             }
6984             return delta;
6985         },
6986
6987         /**
6988          * Returns true if the control, meta, shift or alt key was pressed during this event.
6989          * @return {Boolean}
6990          */
6991         hasModifier : function(){
6992             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6993         },
6994
6995         /**
6996          * Returns true if the target of this event equals el or is a child of el
6997          * @param {String/HTMLElement/Element} el
6998          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6999          * @return {Boolean}
7000          */
7001         within : function(el, related){
7002             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7003             return t && Roo.fly(el).contains(t);
7004         },
7005
7006         getPoint : function(){
7007             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7008         }
7009     };
7010
7011     return new Roo.EventObjectImpl();
7012 }();
7013             
7014     /*
7015  * Based on:
7016  * Ext JS Library 1.1.1
7017  * Copyright(c) 2006-2007, Ext JS, LLC.
7018  *
7019  * Originally Released Under LGPL - original licence link has changed is not relivant.
7020  *
7021  * Fork - LGPL
7022  * <script type="text/javascript">
7023  */
7024
7025  
7026 // was in Composite Element!??!?!
7027  
7028 (function(){
7029     var D = Roo.lib.Dom;
7030     var E = Roo.lib.Event;
7031     var A = Roo.lib.Anim;
7032
7033     // local style camelizing for speed
7034     var propCache = {};
7035     var camelRe = /(-[a-z])/gi;
7036     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7037     var view = document.defaultView;
7038
7039 /**
7040  * @class Roo.Element
7041  * Represents an Element in the DOM.<br><br>
7042  * Usage:<br>
7043 <pre><code>
7044 var el = Roo.get("my-div");
7045
7046 // or with getEl
7047 var el = getEl("my-div");
7048
7049 // or with a DOM element
7050 var el = Roo.get(myDivElement);
7051 </code></pre>
7052  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7053  * each call instead of constructing a new one.<br><br>
7054  * <b>Animations</b><br />
7055  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7056  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7057 <pre>
7058 Option    Default   Description
7059 --------- --------  ---------------------------------------------
7060 duration  .35       The duration of the animation in seconds
7061 easing    easeOut   The YUI easing method
7062 callback  none      A function to execute when the anim completes
7063 scope     this      The scope (this) of the callback function
7064 </pre>
7065 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7066 * manipulate the animation. Here's an example:
7067 <pre><code>
7068 var el = Roo.get("my-div");
7069
7070 // no animation
7071 el.setWidth(100);
7072
7073 // default animation
7074 el.setWidth(100, true);
7075
7076 // animation with some options set
7077 el.setWidth(100, {
7078     duration: 1,
7079     callback: this.foo,
7080     scope: this
7081 });
7082
7083 // using the "anim" property to get the Anim object
7084 var opt = {
7085     duration: 1,
7086     callback: this.foo,
7087     scope: this
7088 };
7089 el.setWidth(100, opt);
7090 ...
7091 if(opt.anim.isAnimated()){
7092     opt.anim.stop();
7093 }
7094 </code></pre>
7095 * <b> Composite (Collections of) Elements</b><br />
7096  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7097  * @constructor Create a new Element directly.
7098  * @param {String/HTMLElement} element
7099  * @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).
7100  */
7101     Roo.Element = function(element, forceNew){
7102         var dom = typeof element == "string" ?
7103                 document.getElementById(element) : element;
7104         if(!dom){ // invalid id/element
7105             return null;
7106         }
7107         var id = dom.id;
7108         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7109             return Roo.Element.cache[id];
7110         }
7111
7112         /**
7113          * The DOM element
7114          * @type HTMLElement
7115          */
7116         this.dom = dom;
7117
7118         /**
7119          * The DOM element ID
7120          * @type String
7121          */
7122         this.id = id || Roo.id(dom);
7123     };
7124
7125     var El = Roo.Element;
7126
7127     El.prototype = {
7128         /**
7129          * The element's default display mode  (defaults to "")
7130          * @type String
7131          */
7132         originalDisplay : "",
7133
7134         visibilityMode : 1,
7135         /**
7136          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7137          * @type String
7138          */
7139         defaultUnit : "px",
7140         
7141         /**
7142          * Sets the element's visibility mode. When setVisible() is called it
7143          * will use this to determine whether to set the visibility or the display property.
7144          * @param visMode Element.VISIBILITY or Element.DISPLAY
7145          * @return {Roo.Element} this
7146          */
7147         setVisibilityMode : function(visMode){
7148             this.visibilityMode = visMode;
7149             return this;
7150         },
7151         /**
7152          * Convenience method for setVisibilityMode(Element.DISPLAY)
7153          * @param {String} display (optional) What to set display to when visible
7154          * @return {Roo.Element} this
7155          */
7156         enableDisplayMode : function(display){
7157             this.setVisibilityMode(El.DISPLAY);
7158             if(typeof display != "undefined") { this.originalDisplay = display; }
7159             return this;
7160         },
7161
7162         /**
7163          * 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)
7164          * @param {String} selector The simple selector to test
7165          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7166                 search as a number or element (defaults to 10 || document.body)
7167          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7168          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7169          */
7170         findParent : function(simpleSelector, maxDepth, returnEl){
7171             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7172             maxDepth = maxDepth || 50;
7173             if(typeof maxDepth != "number"){
7174                 stopEl = Roo.getDom(maxDepth);
7175                 maxDepth = 10;
7176             }
7177             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7178                 if(dq.is(p, simpleSelector)){
7179                     return returnEl ? Roo.get(p) : p;
7180                 }
7181                 depth++;
7182                 p = p.parentNode;
7183             }
7184             return null;
7185         },
7186
7187
7188         /**
7189          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7190          * @param {String} selector The simple selector to test
7191          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7192                 search as a number or element (defaults to 10 || document.body)
7193          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7194          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7195          */
7196         findParentNode : function(simpleSelector, maxDepth, returnEl){
7197             var p = Roo.fly(this.dom.parentNode, '_internal');
7198             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7199         },
7200         
7201         /**
7202          * Looks at  the scrollable parent element
7203          */
7204         findScrollableParent : function()
7205         {
7206             var overflowRegex = /(auto|scroll)/;
7207             
7208             if(this.getStyle('position') === 'fixed'){
7209                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7210             }
7211             
7212             var excludeStaticParent = this.getStyle('position') === "absolute";
7213             
7214             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7215                 
7216                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7217                     continue;
7218                 }
7219                 
7220                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7221                     return parent;
7222                 }
7223                 
7224                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7225                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7226                 }
7227             }
7228             
7229             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7230         },
7231
7232         /**
7233          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7234          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7235          * @param {String} selector The simple selector to test
7236          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7237                 search as a number or element (defaults to 10 || document.body)
7238          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7239          */
7240         up : function(simpleSelector, maxDepth){
7241             return this.findParentNode(simpleSelector, maxDepth, true);
7242         },
7243
7244
7245
7246         /**
7247          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7248          * @param {String} selector The simple selector to test
7249          * @return {Boolean} True if this element matches the selector, else false
7250          */
7251         is : function(simpleSelector){
7252             return Roo.DomQuery.is(this.dom, simpleSelector);
7253         },
7254
7255         /**
7256          * Perform animation on this element.
7257          * @param {Object} args The YUI animation control args
7258          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7259          * @param {Function} onComplete (optional) Function to call when animation completes
7260          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7261          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7262          * @return {Roo.Element} this
7263          */
7264         animate : function(args, duration, onComplete, easing, animType){
7265             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7266             return this;
7267         },
7268
7269         /*
7270          * @private Internal animation call
7271          */
7272         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7273             animType = animType || 'run';
7274             opt = opt || {};
7275             var anim = Roo.lib.Anim[animType](
7276                 this.dom, args,
7277                 (opt.duration || defaultDur) || .35,
7278                 (opt.easing || defaultEase) || 'easeOut',
7279                 function(){
7280                     Roo.callback(cb, this);
7281                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7282                 },
7283                 this
7284             );
7285             opt.anim = anim;
7286             return anim;
7287         },
7288
7289         // private legacy anim prep
7290         preanim : function(a, i){
7291             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7292         },
7293
7294         /**
7295          * Removes worthless text nodes
7296          * @param {Boolean} forceReclean (optional) By default the element
7297          * keeps track if it has been cleaned already so
7298          * you can call this over and over. However, if you update the element and
7299          * need to force a reclean, you can pass true.
7300          */
7301         clean : function(forceReclean){
7302             if(this.isCleaned && forceReclean !== true){
7303                 return this;
7304             }
7305             var ns = /\S/;
7306             var d = this.dom, n = d.firstChild, ni = -1;
7307             while(n){
7308                 var nx = n.nextSibling;
7309                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7310                     d.removeChild(n);
7311                 }else{
7312                     n.nodeIndex = ++ni;
7313                 }
7314                 n = nx;
7315             }
7316             this.isCleaned = true;
7317             return this;
7318         },
7319
7320         // private
7321         calcOffsetsTo : function(el){
7322             el = Roo.get(el);
7323             var d = el.dom;
7324             var restorePos = false;
7325             if(el.getStyle('position') == 'static'){
7326                 el.position('relative');
7327                 restorePos = true;
7328             }
7329             var x = 0, y =0;
7330             var op = this.dom;
7331             while(op && op != d && op.tagName != 'HTML'){
7332                 x+= op.offsetLeft;
7333                 y+= op.offsetTop;
7334                 op = op.offsetParent;
7335             }
7336             if(restorePos){
7337                 el.position('static');
7338             }
7339             return [x, y];
7340         },
7341
7342         /**
7343          * Scrolls this element into view within the passed container.
7344          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7345          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7346          * @return {Roo.Element} this
7347          */
7348         scrollIntoView : function(container, hscroll){
7349             var c = Roo.getDom(container) || document.body;
7350             var el = this.dom;
7351
7352             var o = this.calcOffsetsTo(c),
7353                 l = o[0],
7354                 t = o[1],
7355                 b = t+el.offsetHeight,
7356                 r = l+el.offsetWidth;
7357
7358             var ch = c.clientHeight;
7359             var ct = parseInt(c.scrollTop, 10);
7360             var cl = parseInt(c.scrollLeft, 10);
7361             var cb = ct + ch;
7362             var cr = cl + c.clientWidth;
7363
7364             if(t < ct){
7365                 c.scrollTop = t;
7366             }else if(b > cb){
7367                 c.scrollTop = b-ch;
7368             }
7369
7370             if(hscroll !== false){
7371                 if(l < cl){
7372                     c.scrollLeft = l;
7373                 }else if(r > cr){
7374                     c.scrollLeft = r-c.clientWidth;
7375                 }
7376             }
7377             return this;
7378         },
7379
7380         // private
7381         scrollChildIntoView : function(child, hscroll){
7382             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7383         },
7384
7385         /**
7386          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7387          * the new height may not be available immediately.
7388          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7389          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7390          * @param {Function} onComplete (optional) Function to call when animation completes
7391          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7392          * @return {Roo.Element} this
7393          */
7394         autoHeight : function(animate, duration, onComplete, easing){
7395             var oldHeight = this.getHeight();
7396             this.clip();
7397             this.setHeight(1); // force clipping
7398             setTimeout(function(){
7399                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7400                 if(!animate){
7401                     this.setHeight(height);
7402                     this.unclip();
7403                     if(typeof onComplete == "function"){
7404                         onComplete();
7405                     }
7406                 }else{
7407                     this.setHeight(oldHeight); // restore original height
7408                     this.setHeight(height, animate, duration, function(){
7409                         this.unclip();
7410                         if(typeof onComplete == "function") { onComplete(); }
7411                     }.createDelegate(this), easing);
7412                 }
7413             }.createDelegate(this), 0);
7414             return this;
7415         },
7416
7417         /**
7418          * Returns true if this element is an ancestor of the passed element
7419          * @param {HTMLElement/String} el The element to check
7420          * @return {Boolean} True if this element is an ancestor of el, else false
7421          */
7422         contains : function(el){
7423             if(!el){return false;}
7424             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7425         },
7426
7427         /**
7428          * Checks whether the element is currently visible using both visibility and display properties.
7429          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7430          * @return {Boolean} True if the element is currently visible, else false
7431          */
7432         isVisible : function(deep) {
7433             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7434             if(deep !== true || !vis){
7435                 return vis;
7436             }
7437             var p = this.dom.parentNode;
7438             while(p && p.tagName.toLowerCase() != "body"){
7439                 if(!Roo.fly(p, '_isVisible').isVisible()){
7440                     return false;
7441                 }
7442                 p = p.parentNode;
7443             }
7444             return true;
7445         },
7446
7447         /**
7448          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7449          * @param {String} selector The CSS selector
7450          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7451          * @return {CompositeElement/CompositeElementLite} The composite element
7452          */
7453         select : function(selector, unique){
7454             return El.select(selector, unique, this.dom);
7455         },
7456
7457         /**
7458          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7459          * @param {String} selector The CSS selector
7460          * @return {Array} An array of the matched nodes
7461          */
7462         query : function(selector, unique){
7463             return Roo.DomQuery.select(selector, this.dom);
7464         },
7465
7466         /**
7467          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7468          * @param {String} selector The CSS selector
7469          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7470          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7471          */
7472         child : function(selector, returnDom){
7473             var n = Roo.DomQuery.selectNode(selector, this.dom);
7474             return returnDom ? n : Roo.get(n);
7475         },
7476
7477         /**
7478          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7479          * @param {String} selector The CSS selector
7480          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7481          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7482          */
7483         down : function(selector, returnDom){
7484             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7485             return returnDom ? n : Roo.get(n);
7486         },
7487
7488         /**
7489          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7490          * @param {String} group The group the DD object is member of
7491          * @param {Object} config The DD config object
7492          * @param {Object} overrides An object containing methods to override/implement on the DD object
7493          * @return {Roo.dd.DD} The DD object
7494          */
7495         initDD : function(group, config, overrides){
7496             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7497             return Roo.apply(dd, overrides);
7498         },
7499
7500         /**
7501          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7502          * @param {String} group The group the DDProxy object is member of
7503          * @param {Object} config The DDProxy config object
7504          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7505          * @return {Roo.dd.DDProxy} The DDProxy object
7506          */
7507         initDDProxy : function(group, config, overrides){
7508             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7509             return Roo.apply(dd, overrides);
7510         },
7511
7512         /**
7513          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7514          * @param {String} group The group the DDTarget object is member of
7515          * @param {Object} config The DDTarget config object
7516          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7517          * @return {Roo.dd.DDTarget} The DDTarget object
7518          */
7519         initDDTarget : function(group, config, overrides){
7520             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7521             return Roo.apply(dd, overrides);
7522         },
7523
7524         /**
7525          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7526          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7527          * @param {Boolean} visible Whether the element is visible
7528          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7529          * @return {Roo.Element} this
7530          */
7531          setVisible : function(visible, animate){
7532             if(!animate || !A){
7533                 if(this.visibilityMode == El.DISPLAY){
7534                     this.setDisplayed(visible);
7535                 }else{
7536                     this.fixDisplay();
7537                     this.dom.style.visibility = visible ? "visible" : "hidden";
7538                 }
7539             }else{
7540                 // closure for composites
7541                 var dom = this.dom;
7542                 var visMode = this.visibilityMode;
7543                 if(visible){
7544                     this.setOpacity(.01);
7545                     this.setVisible(true);
7546                 }
7547                 this.anim({opacity: { to: (visible?1:0) }},
7548                       this.preanim(arguments, 1),
7549                       null, .35, 'easeIn', function(){
7550                          if(!visible){
7551                              if(visMode == El.DISPLAY){
7552                                  dom.style.display = "none";
7553                              }else{
7554                                  dom.style.visibility = "hidden";
7555                              }
7556                              Roo.get(dom).setOpacity(1);
7557                          }
7558                      });
7559             }
7560             return this;
7561         },
7562
7563         /**
7564          * Returns true if display is not "none"
7565          * @return {Boolean}
7566          */
7567         isDisplayed : function() {
7568             return this.getStyle("display") != "none";
7569         },
7570
7571         /**
7572          * Toggles the element's visibility or display, depending on visibility mode.
7573          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7574          * @return {Roo.Element} this
7575          */
7576         toggle : function(animate){
7577             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7578             return this;
7579         },
7580
7581         /**
7582          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7583          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7584          * @return {Roo.Element} this
7585          */
7586         setDisplayed : function(value) {
7587             if(typeof value == "boolean"){
7588                value = value ? this.originalDisplay : "none";
7589             }
7590             this.setStyle("display", value);
7591             return this;
7592         },
7593
7594         /**
7595          * Tries to focus the element. Any exceptions are caught and ignored.
7596          * @return {Roo.Element} this
7597          */
7598         focus : function() {
7599             try{
7600                 this.dom.focus();
7601             }catch(e){}
7602             return this;
7603         },
7604
7605         /**
7606          * Tries to blur the element. Any exceptions are caught and ignored.
7607          * @return {Roo.Element} this
7608          */
7609         blur : function() {
7610             try{
7611                 this.dom.blur();
7612             }catch(e){}
7613             return this;
7614         },
7615
7616         /**
7617          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7618          * @param {String/Array} className The CSS class to add, or an array of classes
7619          * @return {Roo.Element} this
7620          */
7621         addClass : function(className){
7622             if(className instanceof Array){
7623                 for(var i = 0, len = className.length; i < len; i++) {
7624                     this.addClass(className[i]);
7625                 }
7626             }else{
7627                 if(className && !this.hasClass(className)){
7628                     this.dom.className = this.dom.className + " " + className;
7629                 }
7630             }
7631             return this;
7632         },
7633
7634         /**
7635          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7636          * @param {String/Array} className The CSS class to add, or an array of classes
7637          * @return {Roo.Element} this
7638          */
7639         radioClass : function(className){
7640             var siblings = this.dom.parentNode.childNodes;
7641             for(var i = 0; i < siblings.length; i++) {
7642                 var s = siblings[i];
7643                 if(s.nodeType == 1){
7644                     Roo.get(s).removeClass(className);
7645                 }
7646             }
7647             this.addClass(className);
7648             return this;
7649         },
7650
7651         /**
7652          * Removes one or more CSS classes from the element.
7653          * @param {String/Array} className The CSS class to remove, or an array of classes
7654          * @return {Roo.Element} this
7655          */
7656         removeClass : function(className){
7657             if(!className || !this.dom.className){
7658                 return this;
7659             }
7660             if(className instanceof Array){
7661                 for(var i = 0, len = className.length; i < len; i++) {
7662                     this.removeClass(className[i]);
7663                 }
7664             }else{
7665                 if(this.hasClass(className)){
7666                     var re = this.classReCache[className];
7667                     if (!re) {
7668                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7669                        this.classReCache[className] = re;
7670                     }
7671                     this.dom.className =
7672                         this.dom.className.replace(re, " ");
7673                 }
7674             }
7675             return this;
7676         },
7677
7678         // private
7679         classReCache: {},
7680
7681         /**
7682          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7683          * @param {String} className The CSS class to toggle
7684          * @return {Roo.Element} this
7685          */
7686         toggleClass : function(className){
7687             if(this.hasClass(className)){
7688                 this.removeClass(className);
7689             }else{
7690                 this.addClass(className);
7691             }
7692             return this;
7693         },
7694
7695         /**
7696          * Checks if the specified CSS class exists on this element's DOM node.
7697          * @param {String} className The CSS class to check for
7698          * @return {Boolean} True if the class exists, else false
7699          */
7700         hasClass : function(className){
7701             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7702         },
7703
7704         /**
7705          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7706          * @param {String} oldClassName The CSS class to replace
7707          * @param {String} newClassName The replacement CSS class
7708          * @return {Roo.Element} this
7709          */
7710         replaceClass : function(oldClassName, newClassName){
7711             this.removeClass(oldClassName);
7712             this.addClass(newClassName);
7713             return this;
7714         },
7715
7716         /**
7717          * Returns an object with properties matching the styles requested.
7718          * For example, el.getStyles('color', 'font-size', 'width') might return
7719          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7720          * @param {String} style1 A style name
7721          * @param {String} style2 A style name
7722          * @param {String} etc.
7723          * @return {Object} The style object
7724          */
7725         getStyles : function(){
7726             var a = arguments, len = a.length, r = {};
7727             for(var i = 0; i < len; i++){
7728                 r[a[i]] = this.getStyle(a[i]);
7729             }
7730             return r;
7731         },
7732
7733         /**
7734          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7735          * @param {String} property The style property whose value is returned.
7736          * @return {String} The current value of the style property for this element.
7737          */
7738         getStyle : function(){
7739             return view && view.getComputedStyle ?
7740                 function(prop){
7741                     var el = this.dom, v, cs, camel;
7742                     if(prop == 'float'){
7743                         prop = "cssFloat";
7744                     }
7745                     if(el.style && (v = el.style[prop])){
7746                         return v;
7747                     }
7748                     if(cs = view.getComputedStyle(el, "")){
7749                         if(!(camel = propCache[prop])){
7750                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7751                         }
7752                         return cs[camel];
7753                     }
7754                     return null;
7755                 } :
7756                 function(prop){
7757                     var el = this.dom, v, cs, camel;
7758                     if(prop == 'opacity'){
7759                         if(typeof el.style.filter == 'string'){
7760                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7761                             if(m){
7762                                 var fv = parseFloat(m[1]);
7763                                 if(!isNaN(fv)){
7764                                     return fv ? fv / 100 : 0;
7765                                 }
7766                             }
7767                         }
7768                         return 1;
7769                     }else if(prop == 'float'){
7770                         prop = "styleFloat";
7771                     }
7772                     if(!(camel = propCache[prop])){
7773                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7774                     }
7775                     if(v = el.style[camel]){
7776                         return v;
7777                     }
7778                     if(cs = el.currentStyle){
7779                         return cs[camel];
7780                     }
7781                     return null;
7782                 };
7783         }(),
7784
7785         /**
7786          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7787          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7788          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7789          * @return {Roo.Element} this
7790          */
7791         setStyle : function(prop, value){
7792             if(typeof prop == "string"){
7793                 
7794                 if (prop == 'float') {
7795                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7796                     return this;
7797                 }
7798                 
7799                 var camel;
7800                 if(!(camel = propCache[prop])){
7801                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7802                 }
7803                 
7804                 if(camel == 'opacity') {
7805                     this.setOpacity(value);
7806                 }else{
7807                     this.dom.style[camel] = value;
7808                 }
7809             }else{
7810                 for(var style in prop){
7811                     if(typeof prop[style] != "function"){
7812                        this.setStyle(style, prop[style]);
7813                     }
7814                 }
7815             }
7816             return this;
7817         },
7818
7819         /**
7820          * More flexible version of {@link #setStyle} for setting style properties.
7821          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7822          * a function which returns such a specification.
7823          * @return {Roo.Element} this
7824          */
7825         applyStyles : function(style){
7826             Roo.DomHelper.applyStyles(this.dom, style);
7827             return this;
7828         },
7829
7830         /**
7831           * 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).
7832           * @return {Number} The X position of the element
7833           */
7834         getX : function(){
7835             return D.getX(this.dom);
7836         },
7837
7838         /**
7839           * 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).
7840           * @return {Number} The Y position of the element
7841           */
7842         getY : function(){
7843             return D.getY(this.dom);
7844         },
7845
7846         /**
7847           * 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).
7848           * @return {Array} The XY position of the element
7849           */
7850         getXY : function(){
7851             return D.getXY(this.dom);
7852         },
7853
7854         /**
7855          * 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).
7856          * @param {Number} The X position of the element
7857          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7858          * @return {Roo.Element} this
7859          */
7860         setX : function(x, animate){
7861             if(!animate || !A){
7862                 D.setX(this.dom, x);
7863             }else{
7864                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7865             }
7866             return this;
7867         },
7868
7869         /**
7870          * 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).
7871          * @param {Number} The Y position of the element
7872          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7873          * @return {Roo.Element} this
7874          */
7875         setY : function(y, animate){
7876             if(!animate || !A){
7877                 D.setY(this.dom, y);
7878             }else{
7879                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7880             }
7881             return this;
7882         },
7883
7884         /**
7885          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7886          * @param {String} left The left CSS property value
7887          * @return {Roo.Element} this
7888          */
7889         setLeft : function(left){
7890             this.setStyle("left", this.addUnits(left));
7891             return this;
7892         },
7893
7894         /**
7895          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7896          * @param {String} top The top CSS property value
7897          * @return {Roo.Element} this
7898          */
7899         setTop : function(top){
7900             this.setStyle("top", this.addUnits(top));
7901             return this;
7902         },
7903
7904         /**
7905          * Sets the element's CSS right style.
7906          * @param {String} right The right CSS property value
7907          * @return {Roo.Element} this
7908          */
7909         setRight : function(right){
7910             this.setStyle("right", this.addUnits(right));
7911             return this;
7912         },
7913
7914         /**
7915          * Sets the element's CSS bottom style.
7916          * @param {String} bottom The bottom CSS property value
7917          * @return {Roo.Element} this
7918          */
7919         setBottom : function(bottom){
7920             this.setStyle("bottom", this.addUnits(bottom));
7921             return this;
7922         },
7923
7924         /**
7925          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7926          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7927          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7928          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7929          * @return {Roo.Element} this
7930          */
7931         setXY : function(pos, animate){
7932             if(!animate || !A){
7933                 D.setXY(this.dom, pos);
7934             }else{
7935                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7936             }
7937             return this;
7938         },
7939
7940         /**
7941          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7942          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7943          * @param {Number} x X value for new position (coordinates are page-based)
7944          * @param {Number} y Y value for new position (coordinates are page-based)
7945          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7946          * @return {Roo.Element} this
7947          */
7948         setLocation : function(x, y, animate){
7949             this.setXY([x, y], this.preanim(arguments, 2));
7950             return this;
7951         },
7952
7953         /**
7954          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7955          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7959          * @return {Roo.Element} this
7960          */
7961         moveTo : function(x, y, animate){
7962             this.setXY([x, y], this.preanim(arguments, 2));
7963             return this;
7964         },
7965
7966         /**
7967          * Returns the region of the given element.
7968          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7969          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7970          */
7971         getRegion : function(){
7972             return D.getRegion(this.dom);
7973         },
7974
7975         /**
7976          * Returns the offset height of the element
7977          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7978          * @return {Number} The element's height
7979          */
7980         getHeight : function(contentHeight){
7981             var h = this.dom.offsetHeight || 0;
7982             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7983         },
7984
7985         /**
7986          * Returns the offset width of the element
7987          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7988          * @return {Number} The element's width
7989          */
7990         getWidth : function(contentWidth){
7991             var w = this.dom.offsetWidth || 0;
7992             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7993         },
7994
7995         /**
7996          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7997          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7998          * if a height has not been set using CSS.
7999          * @return {Number}
8000          */
8001         getComputedHeight : function(){
8002             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8003             if(!h){
8004                 h = parseInt(this.getStyle('height'), 10) || 0;
8005                 if(!this.isBorderBox()){
8006                     h += this.getFrameWidth('tb');
8007                 }
8008             }
8009             return h;
8010         },
8011
8012         /**
8013          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8014          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8015          * if a width has not been set using CSS.
8016          * @return {Number}
8017          */
8018         getComputedWidth : function(){
8019             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8020             if(!w){
8021                 w = parseInt(this.getStyle('width'), 10) || 0;
8022                 if(!this.isBorderBox()){
8023                     w += this.getFrameWidth('lr');
8024                 }
8025             }
8026             return w;
8027         },
8028
8029         /**
8030          * Returns the size of the element.
8031          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8032          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8033          */
8034         getSize : function(contentSize){
8035             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8036         },
8037
8038         /**
8039          * Returns the width and height of the viewport.
8040          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8041          */
8042         getViewSize : function(){
8043             var d = this.dom, doc = document, aw = 0, ah = 0;
8044             if(d == doc || d == doc.body){
8045                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8046             }else{
8047                 return {
8048                     width : d.clientWidth,
8049                     height: d.clientHeight
8050                 };
8051             }
8052         },
8053
8054         /**
8055          * Returns the value of the "value" attribute
8056          * @param {Boolean} asNumber true to parse the value as a number
8057          * @return {String/Number}
8058          */
8059         getValue : function(asNumber){
8060             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8061         },
8062
8063         // private
8064         adjustWidth : function(width){
8065             if(typeof width == "number"){
8066                 if(this.autoBoxAdjust && !this.isBorderBox()){
8067                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8068                 }
8069                 if(width < 0){
8070                     width = 0;
8071                 }
8072             }
8073             return width;
8074         },
8075
8076         // private
8077         adjustHeight : function(height){
8078             if(typeof height == "number"){
8079                if(this.autoBoxAdjust && !this.isBorderBox()){
8080                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8081                }
8082                if(height < 0){
8083                    height = 0;
8084                }
8085             }
8086             return height;
8087         },
8088
8089         /**
8090          * Set the width of the element
8091          * @param {Number} width The new width
8092          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8093          * @return {Roo.Element} this
8094          */
8095         setWidth : function(width, animate){
8096             width = this.adjustWidth(width);
8097             if(!animate || !A){
8098                 this.dom.style.width = this.addUnits(width);
8099             }else{
8100                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8101             }
8102             return this;
8103         },
8104
8105         /**
8106          * Set the height of the element
8107          * @param {Number} height The new height
8108          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8109          * @return {Roo.Element} this
8110          */
8111          setHeight : function(height, animate){
8112             height = this.adjustHeight(height);
8113             if(!animate || !A){
8114                 this.dom.style.height = this.addUnits(height);
8115             }else{
8116                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8117             }
8118             return this;
8119         },
8120
8121         /**
8122          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8123          * @param {Number} width The new width
8124          * @param {Number} height The new height
8125          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8126          * @return {Roo.Element} this
8127          */
8128          setSize : function(width, height, animate){
8129             if(typeof width == "object"){ // in case of object from getSize()
8130                 height = width.height; width = width.width;
8131             }
8132             width = this.adjustWidth(width); height = this.adjustHeight(height);
8133             if(!animate || !A){
8134                 this.dom.style.width = this.addUnits(width);
8135                 this.dom.style.height = this.addUnits(height);
8136             }else{
8137                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8138             }
8139             return this;
8140         },
8141
8142         /**
8143          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8144          * @param {Number} x X value for new position (coordinates are page-based)
8145          * @param {Number} y Y value for new position (coordinates are page-based)
8146          * @param {Number} width The new width
8147          * @param {Number} height The new height
8148          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8149          * @return {Roo.Element} this
8150          */
8151         setBounds : function(x, y, width, height, animate){
8152             if(!animate || !A){
8153                 this.setSize(width, height);
8154                 this.setLocation(x, y);
8155             }else{
8156                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8157                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8158                               this.preanim(arguments, 4), 'motion');
8159             }
8160             return this;
8161         },
8162
8163         /**
8164          * 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.
8165          * @param {Roo.lib.Region} region The region to fill
8166          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8167          * @return {Roo.Element} this
8168          */
8169         setRegion : function(region, animate){
8170             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8171             return this;
8172         },
8173
8174         /**
8175          * Appends an event handler
8176          *
8177          * @param {String}   eventName     The type of event to append
8178          * @param {Function} fn        The method the event invokes
8179          * @param {Object} scope       (optional) The scope (this object) of the fn
8180          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8181          */
8182         addListener : function(eventName, fn, scope, options){
8183             if (this.dom) {
8184                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8185             }
8186         },
8187
8188         /**
8189          * Removes an event handler from this element
8190          * @param {String} eventName the type of event to remove
8191          * @param {Function} fn the method the event invokes
8192          * @return {Roo.Element} this
8193          */
8194         removeListener : function(eventName, fn){
8195             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8196             return this;
8197         },
8198
8199         /**
8200          * Removes all previous added listeners from this element
8201          * @return {Roo.Element} this
8202          */
8203         removeAllListeners : function(){
8204             E.purgeElement(this.dom);
8205             return this;
8206         },
8207
8208         relayEvent : function(eventName, observable){
8209             this.on(eventName, function(e){
8210                 observable.fireEvent(eventName, e);
8211             });
8212         },
8213
8214         /**
8215          * Set the opacity of the element
8216          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8217          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8218          * @return {Roo.Element} this
8219          */
8220          setOpacity : function(opacity, animate){
8221             if(!animate || !A){
8222                 var s = this.dom.style;
8223                 if(Roo.isIE){
8224                     s.zoom = 1;
8225                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8226                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8227                 }else{
8228                     s.opacity = opacity;
8229                 }
8230             }else{
8231                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8232             }
8233             return this;
8234         },
8235
8236         /**
8237          * Gets the left X coordinate
8238          * @param {Boolean} local True to get the local css position instead of page coordinate
8239          * @return {Number}
8240          */
8241         getLeft : function(local){
8242             if(!local){
8243                 return this.getX();
8244             }else{
8245                 return parseInt(this.getStyle("left"), 10) || 0;
8246             }
8247         },
8248
8249         /**
8250          * Gets the right X coordinate of the element (element X position + element width)
8251          * @param {Boolean} local True to get the local css position instead of page coordinate
8252          * @return {Number}
8253          */
8254         getRight : function(local){
8255             if(!local){
8256                 return this.getX() + this.getWidth();
8257             }else{
8258                 return (this.getLeft(true) + this.getWidth()) || 0;
8259             }
8260         },
8261
8262         /**
8263          * Gets the top Y coordinate
8264          * @param {Boolean} local True to get the local css position instead of page coordinate
8265          * @return {Number}
8266          */
8267         getTop : function(local) {
8268             if(!local){
8269                 return this.getY();
8270             }else{
8271                 return parseInt(this.getStyle("top"), 10) || 0;
8272             }
8273         },
8274
8275         /**
8276          * Gets the bottom Y coordinate of the element (element Y position + element height)
8277          * @param {Boolean} local True to get the local css position instead of page coordinate
8278          * @return {Number}
8279          */
8280         getBottom : function(local){
8281             if(!local){
8282                 return this.getY() + this.getHeight();
8283             }else{
8284                 return (this.getTop(true) + this.getHeight()) || 0;
8285             }
8286         },
8287
8288         /**
8289         * Initializes positioning on this element. If a desired position is not passed, it will make the
8290         * the element positioned relative IF it is not already positioned.
8291         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8292         * @param {Number} zIndex (optional) The zIndex to apply
8293         * @param {Number} x (optional) Set the page X position
8294         * @param {Number} y (optional) Set the page Y position
8295         */
8296         position : function(pos, zIndex, x, y){
8297             if(!pos){
8298                if(this.getStyle('position') == 'static'){
8299                    this.setStyle('position', 'relative');
8300                }
8301             }else{
8302                 this.setStyle("position", pos);
8303             }
8304             if(zIndex){
8305                 this.setStyle("z-index", zIndex);
8306             }
8307             if(x !== undefined && y !== undefined){
8308                 this.setXY([x, y]);
8309             }else if(x !== undefined){
8310                 this.setX(x);
8311             }else if(y !== undefined){
8312                 this.setY(y);
8313             }
8314         },
8315
8316         /**
8317         * Clear positioning back to the default when the document was loaded
8318         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8319         * @return {Roo.Element} this
8320          */
8321         clearPositioning : function(value){
8322             value = value ||'';
8323             this.setStyle({
8324                 "left": value,
8325                 "right": value,
8326                 "top": value,
8327                 "bottom": value,
8328                 "z-index": "",
8329                 "position" : "static"
8330             });
8331             return this;
8332         },
8333
8334         /**
8335         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8336         * snapshot before performing an update and then restoring the element.
8337         * @return {Object}
8338         */
8339         getPositioning : function(){
8340             var l = this.getStyle("left");
8341             var t = this.getStyle("top");
8342             return {
8343                 "position" : this.getStyle("position"),
8344                 "left" : l,
8345                 "right" : l ? "" : this.getStyle("right"),
8346                 "top" : t,
8347                 "bottom" : t ? "" : this.getStyle("bottom"),
8348                 "z-index" : this.getStyle("z-index")
8349             };
8350         },
8351
8352         /**
8353          * Gets the width of the border(s) for the specified side(s)
8354          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8355          * passing lr would get the border (l)eft width + the border (r)ight width.
8356          * @return {Number} The width of the sides passed added together
8357          */
8358         getBorderWidth : function(side){
8359             return this.addStyles(side, El.borders);
8360         },
8361
8362         /**
8363          * Gets the width of the padding(s) for the specified side(s)
8364          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8365          * passing lr would get the padding (l)eft + the padding (r)ight.
8366          * @return {Number} The padding of the sides passed added together
8367          */
8368         getPadding : function(side){
8369             return this.addStyles(side, El.paddings);
8370         },
8371
8372         /**
8373         * Set positioning with an object returned by getPositioning().
8374         * @param {Object} posCfg
8375         * @return {Roo.Element} this
8376          */
8377         setPositioning : function(pc){
8378             this.applyStyles(pc);
8379             if(pc.right == "auto"){
8380                 this.dom.style.right = "";
8381             }
8382             if(pc.bottom == "auto"){
8383                 this.dom.style.bottom = "";
8384             }
8385             return this;
8386         },
8387
8388         // private
8389         fixDisplay : function(){
8390             if(this.getStyle("display") == "none"){
8391                 this.setStyle("visibility", "hidden");
8392                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8393                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8394                     this.setStyle("display", "block");
8395                 }
8396             }
8397         },
8398
8399         /**
8400          * Quick set left and top adding default units
8401          * @param {String} left The left CSS property value
8402          * @param {String} top The top CSS property value
8403          * @return {Roo.Element} this
8404          */
8405          setLeftTop : function(left, top){
8406             this.dom.style.left = this.addUnits(left);
8407             this.dom.style.top = this.addUnits(top);
8408             return this;
8409         },
8410
8411         /**
8412          * Move this element relative to its current position.
8413          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8414          * @param {Number} distance How far to move the element in pixels
8415          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8416          * @return {Roo.Element} this
8417          */
8418          move : function(direction, distance, animate){
8419             var xy = this.getXY();
8420             direction = direction.toLowerCase();
8421             switch(direction){
8422                 case "l":
8423                 case "left":
8424                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8425                     break;
8426                case "r":
8427                case "right":
8428                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8429                     break;
8430                case "t":
8431                case "top":
8432                case "up":
8433                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8434                     break;
8435                case "b":
8436                case "bottom":
8437                case "down":
8438                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8439                     break;
8440             }
8441             return this;
8442         },
8443
8444         /**
8445          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8446          * @return {Roo.Element} this
8447          */
8448         clip : function(){
8449             if(!this.isClipped){
8450                this.isClipped = true;
8451                this.originalClip = {
8452                    "o": this.getStyle("overflow"),
8453                    "x": this.getStyle("overflow-x"),
8454                    "y": this.getStyle("overflow-y")
8455                };
8456                this.setStyle("overflow", "hidden");
8457                this.setStyle("overflow-x", "hidden");
8458                this.setStyle("overflow-y", "hidden");
8459             }
8460             return this;
8461         },
8462
8463         /**
8464          *  Return clipping (overflow) to original clipping before clip() was called
8465          * @return {Roo.Element} this
8466          */
8467         unclip : function(){
8468             if(this.isClipped){
8469                 this.isClipped = false;
8470                 var o = this.originalClip;
8471                 if(o.o){this.setStyle("overflow", o.o);}
8472                 if(o.x){this.setStyle("overflow-x", o.x);}
8473                 if(o.y){this.setStyle("overflow-y", o.y);}
8474             }
8475             return this;
8476         },
8477
8478
8479         /**
8480          * Gets the x,y coordinates specified by the anchor position on the element.
8481          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8482          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8483          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8484          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8485          * @return {Array} [x, y] An array containing the element's x and y coordinates
8486          */
8487         getAnchorXY : function(anchor, local, s){
8488             //Passing a different size is useful for pre-calculating anchors,
8489             //especially for anchored animations that change the el size.
8490
8491             var w, h, vp = false;
8492             if(!s){
8493                 var d = this.dom;
8494                 if(d == document.body || d == document){
8495                     vp = true;
8496                     w = D.getViewWidth(); h = D.getViewHeight();
8497                 }else{
8498                     w = this.getWidth(); h = this.getHeight();
8499                 }
8500             }else{
8501                 w = s.width;  h = s.height;
8502             }
8503             var x = 0, y = 0, r = Math.round;
8504             switch((anchor || "tl").toLowerCase()){
8505                 case "c":
8506                     x = r(w*.5);
8507                     y = r(h*.5);
8508                 break;
8509                 case "t":
8510                     x = r(w*.5);
8511                     y = 0;
8512                 break;
8513                 case "l":
8514                     x = 0;
8515                     y = r(h*.5);
8516                 break;
8517                 case "r":
8518                     x = w;
8519                     y = r(h*.5);
8520                 break;
8521                 case "b":
8522                     x = r(w*.5);
8523                     y = h;
8524                 break;
8525                 case "tl":
8526                     x = 0;
8527                     y = 0;
8528                 break;
8529                 case "bl":
8530                     x = 0;
8531                     y = h;
8532                 break;
8533                 case "br":
8534                     x = w;
8535                     y = h;
8536                 break;
8537                 case "tr":
8538                     x = w;
8539                     y = 0;
8540                 break;
8541             }
8542             if(local === true){
8543                 return [x, y];
8544             }
8545             if(vp){
8546                 var sc = this.getScroll();
8547                 return [x + sc.left, y + sc.top];
8548             }
8549             //Add the element's offset xy
8550             var o = this.getXY();
8551             return [x+o[0], y+o[1]];
8552         },
8553
8554         /**
8555          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8556          * supported position values.
8557          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8558          * @param {String} position The position to align to.
8559          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8560          * @return {Array} [x, y]
8561          */
8562         getAlignToXY : function(el, p, o){
8563             el = Roo.get(el);
8564             var d = this.dom;
8565             if(!el.dom){
8566                 throw "Element.alignTo with an element that doesn't exist";
8567             }
8568             var c = false; //constrain to viewport
8569             var p1 = "", p2 = "";
8570             o = o || [0,0];
8571
8572             if(!p){
8573                 p = "tl-bl";
8574             }else if(p == "?"){
8575                 p = "tl-bl?";
8576             }else if(p.indexOf("-") == -1){
8577                 p = "tl-" + p;
8578             }
8579             p = p.toLowerCase();
8580             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8581             if(!m){
8582                throw "Element.alignTo with an invalid alignment " + p;
8583             }
8584             p1 = m[1]; p2 = m[2]; c = !!m[3];
8585
8586             //Subtract the aligned el's internal xy from the target's offset xy
8587             //plus custom offset to get the aligned el's new offset xy
8588             var a1 = this.getAnchorXY(p1, true);
8589             var a2 = el.getAnchorXY(p2, false);
8590             var x = a2[0] - a1[0] + o[0];
8591             var y = a2[1] - a1[1] + o[1];
8592             if(c){
8593                 //constrain the aligned el to viewport if necessary
8594                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8595                 // 5px of margin for ie
8596                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8597
8598                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8599                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8600                 //otherwise swap the aligned el to the opposite border of the target.
8601                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8602                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8603                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8604                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8605
8606                var doc = document;
8607                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8608                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8609
8610                if((x+w) > dw + scrollX){
8611                     x = swapX ? r.left-w : dw+scrollX-w;
8612                 }
8613                if(x < scrollX){
8614                    x = swapX ? r.right : scrollX;
8615                }
8616                if((y+h) > dh + scrollY){
8617                     y = swapY ? r.top-h : dh+scrollY-h;
8618                 }
8619                if (y < scrollY){
8620                    y = swapY ? r.bottom : scrollY;
8621                }
8622             }
8623             return [x,y];
8624         },
8625
8626         // private
8627         getConstrainToXY : function(){
8628             var os = {top:0, left:0, bottom:0, right: 0};
8629
8630             return function(el, local, offsets, proposedXY){
8631                 el = Roo.get(el);
8632                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8633
8634                 var vw, vh, vx = 0, vy = 0;
8635                 if(el.dom == document.body || el.dom == document){
8636                     vw = Roo.lib.Dom.getViewWidth();
8637                     vh = Roo.lib.Dom.getViewHeight();
8638                 }else{
8639                     vw = el.dom.clientWidth;
8640                     vh = el.dom.clientHeight;
8641                     if(!local){
8642                         var vxy = el.getXY();
8643                         vx = vxy[0];
8644                         vy = vxy[1];
8645                     }
8646                 }
8647
8648                 var s = el.getScroll();
8649
8650                 vx += offsets.left + s.left;
8651                 vy += offsets.top + s.top;
8652
8653                 vw -= offsets.right;
8654                 vh -= offsets.bottom;
8655
8656                 var vr = vx+vw;
8657                 var vb = vy+vh;
8658
8659                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8660                 var x = xy[0], y = xy[1];
8661                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8662
8663                 // only move it if it needs it
8664                 var moved = false;
8665
8666                 // first validate right/bottom
8667                 if((x + w) > vr){
8668                     x = vr - w;
8669                     moved = true;
8670                 }
8671                 if((y + h) > vb){
8672                     y = vb - h;
8673                     moved = true;
8674                 }
8675                 // then make sure top/left isn't negative
8676                 if(x < vx){
8677                     x = vx;
8678                     moved = true;
8679                 }
8680                 if(y < vy){
8681                     y = vy;
8682                     moved = true;
8683                 }
8684                 return moved ? [x, y] : false;
8685             };
8686         }(),
8687
8688         // private
8689         adjustForConstraints : function(xy, parent, offsets){
8690             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8691         },
8692
8693         /**
8694          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8695          * document it aligns it to the viewport.
8696          * The position parameter is optional, and can be specified in any one of the following formats:
8697          * <ul>
8698          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8699          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8700          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8701          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8702          *   <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
8703          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8704          * </ul>
8705          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8706          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8707          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8708          * that specified in order to enforce the viewport constraints.
8709          * Following are all of the supported anchor positions:
8710     <pre>
8711     Value  Description
8712     -----  -----------------------------
8713     tl     The top left corner (default)
8714     t      The center of the top edge
8715     tr     The top right corner
8716     l      The center of the left edge
8717     c      In the center of the element
8718     r      The center of the right edge
8719     bl     The bottom left corner
8720     b      The center of the bottom edge
8721     br     The bottom right corner
8722     </pre>
8723     Example Usage:
8724     <pre><code>
8725     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8726     el.alignTo("other-el");
8727
8728     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8729     el.alignTo("other-el", "tr?");
8730
8731     // align the bottom right corner of el with the center left edge of other-el
8732     el.alignTo("other-el", "br-l?");
8733
8734     // align the center of el with the bottom left corner of other-el and
8735     // adjust the x position by -6 pixels (and the y position by 0)
8736     el.alignTo("other-el", "c-bl", [-6, 0]);
8737     </code></pre>
8738          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8739          * @param {String} position The position to align to.
8740          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8741          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8742          * @return {Roo.Element} this
8743          */
8744         alignTo : function(element, position, offsets, animate){
8745             var xy = this.getAlignToXY(element, position, offsets);
8746             this.setXY(xy, this.preanim(arguments, 3));
8747             return this;
8748         },
8749
8750         /**
8751          * Anchors an element to another element and realigns it when the window is resized.
8752          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8753          * @param {String} position The position to align to.
8754          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8755          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8756          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8757          * is a number, it is used as the buffer delay (defaults to 50ms).
8758          * @param {Function} callback The function to call after the animation finishes
8759          * @return {Roo.Element} this
8760          */
8761         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8762             var action = function(){
8763                 this.alignTo(el, alignment, offsets, animate);
8764                 Roo.callback(callback, this);
8765             };
8766             Roo.EventManager.onWindowResize(action, this);
8767             var tm = typeof monitorScroll;
8768             if(tm != 'undefined'){
8769                 Roo.EventManager.on(window, 'scroll', action, this,
8770                     {buffer: tm == 'number' ? monitorScroll : 50});
8771             }
8772             action.call(this); // align immediately
8773             return this;
8774         },
8775         /**
8776          * Clears any opacity settings from this element. Required in some cases for IE.
8777          * @return {Roo.Element} this
8778          */
8779         clearOpacity : function(){
8780             if (window.ActiveXObject) {
8781                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8782                     this.dom.style.filter = "";
8783                 }
8784             } else {
8785                 this.dom.style.opacity = "";
8786                 this.dom.style["-moz-opacity"] = "";
8787                 this.dom.style["-khtml-opacity"] = "";
8788             }
8789             return this;
8790         },
8791
8792         /**
8793          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8794          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8795          * @return {Roo.Element} this
8796          */
8797         hide : function(animate){
8798             this.setVisible(false, this.preanim(arguments, 0));
8799             return this;
8800         },
8801
8802         /**
8803         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8804         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8805          * @return {Roo.Element} this
8806          */
8807         show : function(animate){
8808             this.setVisible(true, this.preanim(arguments, 0));
8809             return this;
8810         },
8811
8812         /**
8813          * @private Test if size has a unit, otherwise appends the default
8814          */
8815         addUnits : function(size){
8816             return Roo.Element.addUnits(size, this.defaultUnit);
8817         },
8818
8819         /**
8820          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8821          * @return {Roo.Element} this
8822          */
8823         beginMeasure : function(){
8824             var el = this.dom;
8825             if(el.offsetWidth || el.offsetHeight){
8826                 return this; // offsets work already
8827             }
8828             var changed = [];
8829             var p = this.dom, b = document.body; // start with this element
8830             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8831                 var pe = Roo.get(p);
8832                 if(pe.getStyle('display') == 'none'){
8833                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8834                     p.style.visibility = "hidden";
8835                     p.style.display = "block";
8836                 }
8837                 p = p.parentNode;
8838             }
8839             this._measureChanged = changed;
8840             return this;
8841
8842         },
8843
8844         /**
8845          * Restores displays to before beginMeasure was called
8846          * @return {Roo.Element} this
8847          */
8848         endMeasure : function(){
8849             var changed = this._measureChanged;
8850             if(changed){
8851                 for(var i = 0, len = changed.length; i < len; i++) {
8852                     var r = changed[i];
8853                     r.el.style.visibility = r.visibility;
8854                     r.el.style.display = "none";
8855                 }
8856                 this._measureChanged = null;
8857             }
8858             return this;
8859         },
8860
8861         /**
8862         * Update the innerHTML of this element, optionally searching for and processing scripts
8863         * @param {String} html The new HTML
8864         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8865         * @param {Function} callback For async script loading you can be noticed when the update completes
8866         * @return {Roo.Element} this
8867          */
8868         update : function(html, loadScripts, callback){
8869             if(typeof html == "undefined"){
8870                 html = "";
8871             }
8872             if(loadScripts !== true){
8873                 this.dom.innerHTML = html;
8874                 if(typeof callback == "function"){
8875                     callback();
8876                 }
8877                 return this;
8878             }
8879             var id = Roo.id();
8880             var dom = this.dom;
8881
8882             html += '<span id="' + id + '"></span>';
8883
8884             E.onAvailable(id, function(){
8885                 var hd = document.getElementsByTagName("head")[0];
8886                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8887                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8888                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8889
8890                 var match;
8891                 while(match = re.exec(html)){
8892                     var attrs = match[1];
8893                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8894                     if(srcMatch && srcMatch[2]){
8895                        var s = document.createElement("script");
8896                        s.src = srcMatch[2];
8897                        var typeMatch = attrs.match(typeRe);
8898                        if(typeMatch && typeMatch[2]){
8899                            s.type = typeMatch[2];
8900                        }
8901                        hd.appendChild(s);
8902                     }else if(match[2] && match[2].length > 0){
8903                         if(window.execScript) {
8904                            window.execScript(match[2]);
8905                         } else {
8906                             /**
8907                              * eval:var:id
8908                              * eval:var:dom
8909                              * eval:var:html
8910                              * 
8911                              */
8912                            window.eval(match[2]);
8913                         }
8914                     }
8915                 }
8916                 var el = document.getElementById(id);
8917                 if(el){el.parentNode.removeChild(el);}
8918                 if(typeof callback == "function"){
8919                     callback();
8920                 }
8921             });
8922             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8923             return this;
8924         },
8925
8926         /**
8927          * Direct access to the UpdateManager update() method (takes the same parameters).
8928          * @param {String/Function} url The url for this request or a function to call to get the url
8929          * @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}
8930          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8931          * @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.
8932          * @return {Roo.Element} this
8933          */
8934         load : function(){
8935             var um = this.getUpdateManager();
8936             um.update.apply(um, arguments);
8937             return this;
8938         },
8939
8940         /**
8941         * Gets this element's UpdateManager
8942         * @return {Roo.UpdateManager} The UpdateManager
8943         */
8944         getUpdateManager : function(){
8945             if(!this.updateManager){
8946                 this.updateManager = new Roo.UpdateManager(this);
8947             }
8948             return this.updateManager;
8949         },
8950
8951         /**
8952          * Disables text selection for this element (normalized across browsers)
8953          * @return {Roo.Element} this
8954          */
8955         unselectable : function(){
8956             this.dom.unselectable = "on";
8957             this.swallowEvent("selectstart", true);
8958             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8959             this.addClass("x-unselectable");
8960             return this;
8961         },
8962
8963         /**
8964         * Calculates the x, y to center this element on the screen
8965         * @return {Array} The x, y values [x, y]
8966         */
8967         getCenterXY : function(){
8968             return this.getAlignToXY(document, 'c-c');
8969         },
8970
8971         /**
8972         * Centers the Element in either the viewport, or another Element.
8973         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8974         */
8975         center : function(centerIn){
8976             this.alignTo(centerIn || document, 'c-c');
8977             return this;
8978         },
8979
8980         /**
8981          * Tests various css rules/browsers to determine if this element uses a border box
8982          * @return {Boolean}
8983          */
8984         isBorderBox : function(){
8985             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8986         },
8987
8988         /**
8989          * Return a box {x, y, width, height} that can be used to set another elements
8990          * size/location to match this element.
8991          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8992          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8993          * @return {Object} box An object in the format {x, y, width, height}
8994          */
8995         getBox : function(contentBox, local){
8996             var xy;
8997             if(!local){
8998                 xy = this.getXY();
8999             }else{
9000                 var left = parseInt(this.getStyle("left"), 10) || 0;
9001                 var top = parseInt(this.getStyle("top"), 10) || 0;
9002                 xy = [left, top];
9003             }
9004             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9005             if(!contentBox){
9006                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9007             }else{
9008                 var l = this.getBorderWidth("l")+this.getPadding("l");
9009                 var r = this.getBorderWidth("r")+this.getPadding("r");
9010                 var t = this.getBorderWidth("t")+this.getPadding("t");
9011                 var b = this.getBorderWidth("b")+this.getPadding("b");
9012                 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)};
9013             }
9014             bx.right = bx.x + bx.width;
9015             bx.bottom = bx.y + bx.height;
9016             return bx;
9017         },
9018
9019         /**
9020          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9021          for more information about the sides.
9022          * @param {String} sides
9023          * @return {Number}
9024          */
9025         getFrameWidth : function(sides, onlyContentBox){
9026             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9027         },
9028
9029         /**
9030          * 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.
9031          * @param {Object} box The box to fill {x, y, width, height}
9032          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9033          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9034          * @return {Roo.Element} this
9035          */
9036         setBox : function(box, adjust, animate){
9037             var w = box.width, h = box.height;
9038             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9039                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9040                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9041             }
9042             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9043             return this;
9044         },
9045
9046         /**
9047          * Forces the browser to repaint this element
9048          * @return {Roo.Element} this
9049          */
9050          repaint : function(){
9051             var dom = this.dom;
9052             this.addClass("x-repaint");
9053             setTimeout(function(){
9054                 Roo.get(dom).removeClass("x-repaint");
9055             }, 1);
9056             return this;
9057         },
9058
9059         /**
9060          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9061          * then it returns the calculated width of the sides (see getPadding)
9062          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9063          * @return {Object/Number}
9064          */
9065         getMargins : function(side){
9066             if(!side){
9067                 return {
9068                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9069                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9070                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9071                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9072                 };
9073             }else{
9074                 return this.addStyles(side, El.margins);
9075              }
9076         },
9077
9078         // private
9079         addStyles : function(sides, styles){
9080             var val = 0, v, w;
9081             for(var i = 0, len = sides.length; i < len; i++){
9082                 v = this.getStyle(styles[sides.charAt(i)]);
9083                 if(v){
9084                      w = parseInt(v, 10);
9085                      if(w){ val += w; }
9086                 }
9087             }
9088             return val;
9089         },
9090
9091         /**
9092          * Creates a proxy element of this element
9093          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9094          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9095          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9096          * @return {Roo.Element} The new proxy element
9097          */
9098         createProxy : function(config, renderTo, matchBox){
9099             if(renderTo){
9100                 renderTo = Roo.getDom(renderTo);
9101             }else{
9102                 renderTo = document.body;
9103             }
9104             config = typeof config == "object" ?
9105                 config : {tag : "div", cls: config};
9106             var proxy = Roo.DomHelper.append(renderTo, config, true);
9107             if(matchBox){
9108                proxy.setBox(this.getBox());
9109             }
9110             return proxy;
9111         },
9112
9113         /**
9114          * Puts a mask over this element to disable user interaction. Requires core.css.
9115          * This method can only be applied to elements which accept child nodes.
9116          * @param {String} msg (optional) A message to display in the mask
9117          * @param {String} msgCls (optional) A css class to apply to the msg element
9118          * @return {Element} The mask  element
9119          */
9120         mask : function(msg, msgCls)
9121         {
9122             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9123                 this.setStyle("position", "relative");
9124             }
9125             if(!this._mask){
9126                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9127             }
9128             
9129             this.addClass("x-masked");
9130             this._mask.setDisplayed(true);
9131             
9132             // we wander
9133             var z = 0;
9134             var dom = this.dom;
9135             while (dom && dom.style) {
9136                 if (!isNaN(parseInt(dom.style.zIndex))) {
9137                     z = Math.max(z, parseInt(dom.style.zIndex));
9138                 }
9139                 dom = dom.parentNode;
9140             }
9141             // if we are masking the body - then it hides everything..
9142             if (this.dom == document.body) {
9143                 z = 1000000;
9144                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9145                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9146             }
9147            
9148             if(typeof msg == 'string'){
9149                 if(!this._maskMsg){
9150                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9151                         cls: "roo-el-mask-msg", 
9152                         cn: [
9153                             {
9154                                 tag: 'i',
9155                                 cls: 'fa fa-spinner fa-spin'
9156                             },
9157                             {
9158                                 tag: 'div'
9159                             }   
9160                         ]
9161                     }, true);
9162                 }
9163                 var mm = this._maskMsg;
9164                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9165                 if (mm.dom.lastChild) { // weird IE issue?
9166                     mm.dom.lastChild.innerHTML = msg;
9167                 }
9168                 mm.setDisplayed(true);
9169                 mm.center(this);
9170                 mm.setStyle('z-index', z + 102);
9171             }
9172             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9173                 this._mask.setHeight(this.getHeight());
9174             }
9175             this._mask.setStyle('z-index', z + 100);
9176             
9177             return this._mask;
9178         },
9179
9180         /**
9181          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9182          * it is cached for reuse.
9183          */
9184         unmask : function(removeEl){
9185             if(this._mask){
9186                 if(removeEl === true){
9187                     this._mask.remove();
9188                     delete this._mask;
9189                     if(this._maskMsg){
9190                         this._maskMsg.remove();
9191                         delete this._maskMsg;
9192                     }
9193                 }else{
9194                     this._mask.setDisplayed(false);
9195                     if(this._maskMsg){
9196                         this._maskMsg.setDisplayed(false);
9197                     }
9198                 }
9199             }
9200             this.removeClass("x-masked");
9201         },
9202
9203         /**
9204          * Returns true if this element is masked
9205          * @return {Boolean}
9206          */
9207         isMasked : function(){
9208             return this._mask && this._mask.isVisible();
9209         },
9210
9211         /**
9212          * Creates an iframe shim for this element to keep selects and other windowed objects from
9213          * showing through.
9214          * @return {Roo.Element} The new shim element
9215          */
9216         createShim : function(){
9217             var el = document.createElement('iframe');
9218             el.frameBorder = 'no';
9219             el.className = 'roo-shim';
9220             if(Roo.isIE && Roo.isSecure){
9221                 el.src = Roo.SSL_SECURE_URL;
9222             }
9223             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9224             shim.autoBoxAdjust = false;
9225             return shim;
9226         },
9227
9228         /**
9229          * Removes this element from the DOM and deletes it from the cache
9230          */
9231         remove : function(){
9232             if(this.dom.parentNode){
9233                 this.dom.parentNode.removeChild(this.dom);
9234             }
9235             delete El.cache[this.dom.id];
9236         },
9237
9238         /**
9239          * Sets up event handlers to add and remove a css class when the mouse is over this element
9240          * @param {String} className
9241          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9242          * mouseout events for children elements
9243          * @return {Roo.Element} this
9244          */
9245         addClassOnOver : function(className, preventFlicker){
9246             this.on("mouseover", function(){
9247                 Roo.fly(this, '_internal').addClass(className);
9248             }, this.dom);
9249             var removeFn = function(e){
9250                 if(preventFlicker !== true || !e.within(this, true)){
9251                     Roo.fly(this, '_internal').removeClass(className);
9252                 }
9253             };
9254             this.on("mouseout", removeFn, this.dom);
9255             return this;
9256         },
9257
9258         /**
9259          * Sets up event handlers to add and remove a css class when this element has the focus
9260          * @param {String} className
9261          * @return {Roo.Element} this
9262          */
9263         addClassOnFocus : function(className){
9264             this.on("focus", function(){
9265                 Roo.fly(this, '_internal').addClass(className);
9266             }, this.dom);
9267             this.on("blur", function(){
9268                 Roo.fly(this, '_internal').removeClass(className);
9269             }, this.dom);
9270             return this;
9271         },
9272         /**
9273          * 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)
9274          * @param {String} className
9275          * @return {Roo.Element} this
9276          */
9277         addClassOnClick : function(className){
9278             var dom = this.dom;
9279             this.on("mousedown", function(){
9280                 Roo.fly(dom, '_internal').addClass(className);
9281                 var d = Roo.get(document);
9282                 var fn = function(){
9283                     Roo.fly(dom, '_internal').removeClass(className);
9284                     d.removeListener("mouseup", fn);
9285                 };
9286                 d.on("mouseup", fn);
9287             });
9288             return this;
9289         },
9290
9291         /**
9292          * Stops the specified event from bubbling and optionally prevents the default action
9293          * @param {String} eventName
9294          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9295          * @return {Roo.Element} this
9296          */
9297         swallowEvent : function(eventName, preventDefault){
9298             var fn = function(e){
9299                 e.stopPropagation();
9300                 if(preventDefault){
9301                     e.preventDefault();
9302                 }
9303             };
9304             if(eventName instanceof Array){
9305                 for(var i = 0, len = eventName.length; i < len; i++){
9306                      this.on(eventName[i], fn);
9307                 }
9308                 return this;
9309             }
9310             this.on(eventName, fn);
9311             return this;
9312         },
9313
9314         /**
9315          * @private
9316          */
9317       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9318
9319         /**
9320          * Sizes this element to its parent element's dimensions performing
9321          * neccessary box adjustments.
9322          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9323          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9324          * @return {Roo.Element} this
9325          */
9326         fitToParent : function(monitorResize, targetParent) {
9327           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9328           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9329           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9330             return;
9331           }
9332           var p = Roo.get(targetParent || this.dom.parentNode);
9333           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9334           if (monitorResize === true) {
9335             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9336             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9337           }
9338           return this;
9339         },
9340
9341         /**
9342          * Gets the next sibling, skipping text nodes
9343          * @return {HTMLElement} The next sibling or null
9344          */
9345         getNextSibling : function(){
9346             var n = this.dom.nextSibling;
9347             while(n && n.nodeType != 1){
9348                 n = n.nextSibling;
9349             }
9350             return n;
9351         },
9352
9353         /**
9354          * Gets the previous sibling, skipping text nodes
9355          * @return {HTMLElement} The previous sibling or null
9356          */
9357         getPrevSibling : function(){
9358             var n = this.dom.previousSibling;
9359             while(n && n.nodeType != 1){
9360                 n = n.previousSibling;
9361             }
9362             return n;
9363         },
9364
9365
9366         /**
9367          * Appends the passed element(s) to this element
9368          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9369          * @return {Roo.Element} this
9370          */
9371         appendChild: function(el){
9372             el = Roo.get(el);
9373             el.appendTo(this);
9374             return this;
9375         },
9376
9377         /**
9378          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9379          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9380          * automatically generated with the specified attributes.
9381          * @param {HTMLElement} insertBefore (optional) a child element of this element
9382          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9383          * @return {Roo.Element} The new child element
9384          */
9385         createChild: function(config, insertBefore, returnDom){
9386             config = config || {tag:'div'};
9387             if(insertBefore){
9388                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9389             }
9390             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9391         },
9392
9393         /**
9394          * Appends this element to the passed element
9395          * @param {String/HTMLElement/Element} el The new parent element
9396          * @return {Roo.Element} this
9397          */
9398         appendTo: function(el){
9399             el = Roo.getDom(el);
9400             el.appendChild(this.dom);
9401             return this;
9402         },
9403
9404         /**
9405          * Inserts this element before the passed element in the DOM
9406          * @param {String/HTMLElement/Element} el The element to insert before
9407          * @return {Roo.Element} this
9408          */
9409         insertBefore: function(el){
9410             el = Roo.getDom(el);
9411             el.parentNode.insertBefore(this.dom, el);
9412             return this;
9413         },
9414
9415         /**
9416          * Inserts this element after the passed element in the DOM
9417          * @param {String/HTMLElement/Element} el The element to insert after
9418          * @return {Roo.Element} this
9419          */
9420         insertAfter: function(el){
9421             el = Roo.getDom(el);
9422             el.parentNode.insertBefore(this.dom, el.nextSibling);
9423             return this;
9424         },
9425
9426         /**
9427          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9428          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9429          * @return {Roo.Element} The new child
9430          */
9431         insertFirst: function(el, returnDom){
9432             el = el || {};
9433             if(typeof el == 'object' && !el.nodeType){ // dh config
9434                 return this.createChild(el, this.dom.firstChild, returnDom);
9435             }else{
9436                 el = Roo.getDom(el);
9437                 this.dom.insertBefore(el, this.dom.firstChild);
9438                 return !returnDom ? Roo.get(el) : el;
9439             }
9440         },
9441
9442         /**
9443          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9444          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9445          * @param {String} where (optional) 'before' or 'after' defaults to before
9446          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9447          * @return {Roo.Element} the inserted Element
9448          */
9449         insertSibling: function(el, where, returnDom){
9450             where = where ? where.toLowerCase() : 'before';
9451             el = el || {};
9452             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9453
9454             if(typeof el == 'object' && !el.nodeType){ // dh config
9455                 if(where == 'after' && !this.dom.nextSibling){
9456                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9457                 }else{
9458                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9459                 }
9460
9461             }else{
9462                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9463                             where == 'before' ? this.dom : this.dom.nextSibling);
9464                 if(!returnDom){
9465                     rt = Roo.get(rt);
9466                 }
9467             }
9468             return rt;
9469         },
9470
9471         /**
9472          * Creates and wraps this element with another element
9473          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9474          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9475          * @return {HTMLElement/Element} The newly created wrapper element
9476          */
9477         wrap: function(config, returnDom){
9478             if(!config){
9479                 config = {tag: "div"};
9480             }
9481             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9482             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9483             return newEl;
9484         },
9485
9486         /**
9487          * Replaces the passed element with this element
9488          * @param {String/HTMLElement/Element} el The element to replace
9489          * @return {Roo.Element} this
9490          */
9491         replace: function(el){
9492             el = Roo.get(el);
9493             this.insertBefore(el);
9494             el.remove();
9495             return this;
9496         },
9497
9498         /**
9499          * Inserts an html fragment into this element
9500          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9501          * @param {String} html The HTML fragment
9502          * @param {Boolean} returnEl True to return an Roo.Element
9503          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9504          */
9505         insertHtml : function(where, html, returnEl){
9506             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9507             return returnEl ? Roo.get(el) : el;
9508         },
9509
9510         /**
9511          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9512          * @param {Object} o The object with the attributes
9513          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9514          * @return {Roo.Element} this
9515          */
9516         set : function(o, useSet){
9517             var el = this.dom;
9518             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9519             for(var attr in o){
9520                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9521                 if(attr=="cls"){
9522                     el.className = o["cls"];
9523                 }else{
9524                     if(useSet) {
9525                         el.setAttribute(attr, o[attr]);
9526                     } else {
9527                         el[attr] = o[attr];
9528                     }
9529                 }
9530             }
9531             if(o.style){
9532                 Roo.DomHelper.applyStyles(el, o.style);
9533             }
9534             return this;
9535         },
9536
9537         /**
9538          * Convenience method for constructing a KeyMap
9539          * @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:
9540          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9541          * @param {Function} fn The function to call
9542          * @param {Object} scope (optional) The scope of the function
9543          * @return {Roo.KeyMap} The KeyMap created
9544          */
9545         addKeyListener : function(key, fn, scope){
9546             var config;
9547             if(typeof key != "object" || key instanceof Array){
9548                 config = {
9549                     key: key,
9550                     fn: fn,
9551                     scope: scope
9552                 };
9553             }else{
9554                 config = {
9555                     key : key.key,
9556                     shift : key.shift,
9557                     ctrl : key.ctrl,
9558                     alt : key.alt,
9559                     fn: fn,
9560                     scope: scope
9561                 };
9562             }
9563             return new Roo.KeyMap(this, config);
9564         },
9565
9566         /**
9567          * Creates a KeyMap for this element
9568          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9569          * @return {Roo.KeyMap} The KeyMap created
9570          */
9571         addKeyMap : function(config){
9572             return new Roo.KeyMap(this, config);
9573         },
9574
9575         /**
9576          * Returns true if this element is scrollable.
9577          * @return {Boolean}
9578          */
9579          isScrollable : function(){
9580             var dom = this.dom;
9581             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9582         },
9583
9584         /**
9585          * 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().
9586          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9587          * @param {Number} value The new scroll value
9588          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9589          * @return {Element} this
9590          */
9591
9592         scrollTo : function(side, value, animate){
9593             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9594             if(!animate || !A){
9595                 this.dom[prop] = value;
9596             }else{
9597                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9598                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9599             }
9600             return this;
9601         },
9602
9603         /**
9604          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9605          * within this element's scrollable range.
9606          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9607          * @param {Number} distance How far to scroll the element in pixels
9608          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9609          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9610          * was scrolled as far as it could go.
9611          */
9612          scroll : function(direction, distance, animate){
9613              if(!this.isScrollable()){
9614                  return;
9615              }
9616              var el = this.dom;
9617              var l = el.scrollLeft, t = el.scrollTop;
9618              var w = el.scrollWidth, h = el.scrollHeight;
9619              var cw = el.clientWidth, ch = el.clientHeight;
9620              direction = direction.toLowerCase();
9621              var scrolled = false;
9622              var a = this.preanim(arguments, 2);
9623              switch(direction){
9624                  case "l":
9625                  case "left":
9626                      if(w - l > cw){
9627                          var v = Math.min(l + distance, w-cw);
9628                          this.scrollTo("left", v, a);
9629                          scrolled = true;
9630                      }
9631                      break;
9632                 case "r":
9633                 case "right":
9634                      if(l > 0){
9635                          var v = Math.max(l - distance, 0);
9636                          this.scrollTo("left", v, a);
9637                          scrolled = true;
9638                      }
9639                      break;
9640                 case "t":
9641                 case "top":
9642                 case "up":
9643                      if(t > 0){
9644                          var v = Math.max(t - distance, 0);
9645                          this.scrollTo("top", v, a);
9646                          scrolled = true;
9647                      }
9648                      break;
9649                 case "b":
9650                 case "bottom":
9651                 case "down":
9652                      if(h - t > ch){
9653                          var v = Math.min(t + distance, h-ch);
9654                          this.scrollTo("top", v, a);
9655                          scrolled = true;
9656                      }
9657                      break;
9658              }
9659              return scrolled;
9660         },
9661
9662         /**
9663          * Translates the passed page coordinates into left/top css values for this element
9664          * @param {Number/Array} x The page x or an array containing [x, y]
9665          * @param {Number} y The page y
9666          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9667          */
9668         translatePoints : function(x, y){
9669             if(typeof x == 'object' || x instanceof Array){
9670                 y = x[1]; x = x[0];
9671             }
9672             var p = this.getStyle('position');
9673             var o = this.getXY();
9674
9675             var l = parseInt(this.getStyle('left'), 10);
9676             var t = parseInt(this.getStyle('top'), 10);
9677
9678             if(isNaN(l)){
9679                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9680             }
9681             if(isNaN(t)){
9682                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9683             }
9684
9685             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9686         },
9687
9688         /**
9689          * Returns the current scroll position of the element.
9690          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9691          */
9692         getScroll : function(){
9693             var d = this.dom, doc = document;
9694             if(d == doc || d == doc.body){
9695                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9696                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9697                 return {left: l, top: t};
9698             }else{
9699                 return {left: d.scrollLeft, top: d.scrollTop};
9700             }
9701         },
9702
9703         /**
9704          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9705          * are convert to standard 6 digit hex color.
9706          * @param {String} attr The css attribute
9707          * @param {String} defaultValue The default value to use when a valid color isn't found
9708          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9709          * YUI color anims.
9710          */
9711         getColor : function(attr, defaultValue, prefix){
9712             var v = this.getStyle(attr);
9713             if(!v || v == "transparent" || v == "inherit") {
9714                 return defaultValue;
9715             }
9716             var color = typeof prefix == "undefined" ? "#" : prefix;
9717             if(v.substr(0, 4) == "rgb("){
9718                 var rvs = v.slice(4, v.length -1).split(",");
9719                 for(var i = 0; i < 3; i++){
9720                     var h = parseInt(rvs[i]).toString(16);
9721                     if(h < 16){
9722                         h = "0" + h;
9723                     }
9724                     color += h;
9725                 }
9726             } else {
9727                 if(v.substr(0, 1) == "#"){
9728                     if(v.length == 4) {
9729                         for(var i = 1; i < 4; i++){
9730                             var c = v.charAt(i);
9731                             color +=  c + c;
9732                         }
9733                     }else if(v.length == 7){
9734                         color += v.substr(1);
9735                     }
9736                 }
9737             }
9738             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9739         },
9740
9741         /**
9742          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9743          * gradient background, rounded corners and a 4-way shadow.
9744          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9745          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9746          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9747          * @return {Roo.Element} this
9748          */
9749         boxWrap : function(cls){
9750             cls = cls || 'x-box';
9751             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9752             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9753             return el;
9754         },
9755
9756         /**
9757          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9758          * @param {String} namespace The namespace in which to look for the attribute
9759          * @param {String} name The attribute name
9760          * @return {String} The attribute value
9761          */
9762         getAttributeNS : Roo.isIE ? function(ns, name){
9763             var d = this.dom;
9764             var type = typeof d[ns+":"+name];
9765             if(type != 'undefined' && type != 'unknown'){
9766                 return d[ns+":"+name];
9767             }
9768             return d[name];
9769         } : function(ns, name){
9770             var d = this.dom;
9771             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9772         },
9773         
9774         
9775         /**
9776          * Sets or Returns the value the dom attribute value
9777          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9778          * @param {String} value (optional) The value to set the attribute to
9779          * @return {String} The attribute value
9780          */
9781         attr : function(name){
9782             if (arguments.length > 1) {
9783                 this.dom.setAttribute(name, arguments[1]);
9784                 return arguments[1];
9785             }
9786             if (typeof(name) == 'object') {
9787                 for(var i in name) {
9788                     this.attr(i, name[i]);
9789                 }
9790                 return name;
9791             }
9792             
9793             
9794             if (!this.dom.hasAttribute(name)) {
9795                 return undefined;
9796             }
9797             return this.dom.getAttribute(name);
9798         }
9799         
9800         
9801         
9802     };
9803
9804     var ep = El.prototype;
9805
9806     /**
9807      * Appends an event handler (Shorthand for addListener)
9808      * @param {String}   eventName     The type of event to append
9809      * @param {Function} fn        The method the event invokes
9810      * @param {Object} scope       (optional) The scope (this object) of the fn
9811      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9812      * @method
9813      */
9814     ep.on = ep.addListener;
9815         // backwards compat
9816     ep.mon = ep.addListener;
9817
9818     /**
9819      * Removes an event handler from this element (shorthand for removeListener)
9820      * @param {String} eventName the type of event to remove
9821      * @param {Function} fn the method the event invokes
9822      * @return {Roo.Element} this
9823      * @method
9824      */
9825     ep.un = ep.removeListener;
9826
9827     /**
9828      * true to automatically adjust width and height settings for box-model issues (default to true)
9829      */
9830     ep.autoBoxAdjust = true;
9831
9832     // private
9833     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9834
9835     // private
9836     El.addUnits = function(v, defaultUnit){
9837         if(v === "" || v == "auto"){
9838             return v;
9839         }
9840         if(v === undefined){
9841             return '';
9842         }
9843         if(typeof v == "number" || !El.unitPattern.test(v)){
9844             return v + (defaultUnit || 'px');
9845         }
9846         return v;
9847     };
9848
9849     // special markup used throughout Roo when box wrapping elements
9850     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>';
9851     /**
9852      * Visibility mode constant - Use visibility to hide element
9853      * @static
9854      * @type Number
9855      */
9856     El.VISIBILITY = 1;
9857     /**
9858      * Visibility mode constant - Use display to hide element
9859      * @static
9860      * @type Number
9861      */
9862     El.DISPLAY = 2;
9863
9864     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9865     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9866     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9867
9868
9869
9870     /**
9871      * @private
9872      */
9873     El.cache = {};
9874
9875     var docEl;
9876
9877     /**
9878      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9879      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9880      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9881      * @return {Element} The Element object
9882      * @static
9883      */
9884     El.get = function(el){
9885         var ex, elm, id;
9886         if(!el){ return null; }
9887         if(typeof el == "string"){ // element id
9888             if(!(elm = document.getElementById(el))){
9889                 return null;
9890             }
9891             if(ex = El.cache[el]){
9892                 ex.dom = elm;
9893             }else{
9894                 ex = El.cache[el] = new El(elm);
9895             }
9896             return ex;
9897         }else if(el.tagName){ // dom element
9898             if(!(id = el.id)){
9899                 id = Roo.id(el);
9900             }
9901             if(ex = El.cache[id]){
9902                 ex.dom = el;
9903             }else{
9904                 ex = El.cache[id] = new El(el);
9905             }
9906             return ex;
9907         }else if(el instanceof El){
9908             if(el != docEl){
9909                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9910                                                               // catch case where it hasn't been appended
9911                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9912             }
9913             return el;
9914         }else if(el.isComposite){
9915             return el;
9916         }else if(el instanceof Array){
9917             return El.select(el);
9918         }else if(el == document){
9919             // create a bogus element object representing the document object
9920             if(!docEl){
9921                 var f = function(){};
9922                 f.prototype = El.prototype;
9923                 docEl = new f();
9924                 docEl.dom = document;
9925             }
9926             return docEl;
9927         }
9928         return null;
9929     };
9930
9931     // private
9932     El.uncache = function(el){
9933         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9934             if(a[i]){
9935                 delete El.cache[a[i].id || a[i]];
9936             }
9937         }
9938     };
9939
9940     // private
9941     // Garbage collection - uncache elements/purge listeners on orphaned elements
9942     // so we don't hold a reference and cause the browser to retain them
9943     El.garbageCollect = function(){
9944         if(!Roo.enableGarbageCollector){
9945             clearInterval(El.collectorThread);
9946             return;
9947         }
9948         for(var eid in El.cache){
9949             var el = El.cache[eid], d = el.dom;
9950             // -------------------------------------------------------
9951             // Determining what is garbage:
9952             // -------------------------------------------------------
9953             // !d
9954             // dom node is null, definitely garbage
9955             // -------------------------------------------------------
9956             // !d.parentNode
9957             // no parentNode == direct orphan, definitely garbage
9958             // -------------------------------------------------------
9959             // !d.offsetParent && !document.getElementById(eid)
9960             // display none elements have no offsetParent so we will
9961             // also try to look it up by it's id. However, check
9962             // offsetParent first so we don't do unneeded lookups.
9963             // This enables collection of elements that are not orphans
9964             // directly, but somewhere up the line they have an orphan
9965             // parent.
9966             // -------------------------------------------------------
9967             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9968                 delete El.cache[eid];
9969                 if(d && Roo.enableListenerCollection){
9970                     E.purgeElement(d);
9971                 }
9972             }
9973         }
9974     }
9975     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9976
9977
9978     // dom is optional
9979     El.Flyweight = function(dom){
9980         this.dom = dom;
9981     };
9982     El.Flyweight.prototype = El.prototype;
9983
9984     El._flyweights = {};
9985     /**
9986      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9987      * the dom node can be overwritten by other code.
9988      * @param {String/HTMLElement} el The dom node or id
9989      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9990      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9991      * @static
9992      * @return {Element} The shared Element object
9993      */
9994     El.fly = function(el, named){
9995         named = named || '_global';
9996         el = Roo.getDom(el);
9997         if(!el){
9998             return null;
9999         }
10000         if(!El._flyweights[named]){
10001             El._flyweights[named] = new El.Flyweight();
10002         }
10003         El._flyweights[named].dom = el;
10004         return El._flyweights[named];
10005     };
10006
10007     /**
10008      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10009      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10010      * Shorthand of {@link Roo.Element#get}
10011      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10012      * @return {Element} The Element object
10013      * @member Roo
10014      * @method get
10015      */
10016     Roo.get = El.get;
10017     /**
10018      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10019      * the dom node can be overwritten by other code.
10020      * Shorthand of {@link Roo.Element#fly}
10021      * @param {String/HTMLElement} el The dom node or id
10022      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10023      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10024      * @static
10025      * @return {Element} The shared Element object
10026      * @member Roo
10027      * @method fly
10028      */
10029     Roo.fly = El.fly;
10030
10031     // speedy lookup for elements never to box adjust
10032     var noBoxAdjust = Roo.isStrict ? {
10033         select:1
10034     } : {
10035         input:1, select:1, textarea:1
10036     };
10037     if(Roo.isIE || Roo.isGecko){
10038         noBoxAdjust['button'] = 1;
10039     }
10040
10041
10042     Roo.EventManager.on(window, 'unload', function(){
10043         delete El.cache;
10044         delete El._flyweights;
10045     });
10046 })();
10047
10048
10049
10050
10051 if(Roo.DomQuery){
10052     Roo.Element.selectorFunction = Roo.DomQuery.select;
10053 }
10054
10055 Roo.Element.select = function(selector, unique, root){
10056     var els;
10057     if(typeof selector == "string"){
10058         els = Roo.Element.selectorFunction(selector, root);
10059     }else if(selector.length !== undefined){
10060         els = selector;
10061     }else{
10062         throw "Invalid selector";
10063     }
10064     if(unique === true){
10065         return new Roo.CompositeElement(els);
10066     }else{
10067         return new Roo.CompositeElementLite(els);
10068     }
10069 };
10070 /**
10071  * Selects elements based on the passed CSS selector to enable working on them as 1.
10072  * @param {String/Array} selector The CSS selector or an array of elements
10073  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10074  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10075  * @return {CompositeElementLite/CompositeElement}
10076  * @member Roo
10077  * @method select
10078  */
10079 Roo.select = Roo.Element.select;
10080
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094 /*
10095  * Based on:
10096  * Ext JS Library 1.1.1
10097  * Copyright(c) 2006-2007, Ext JS, LLC.
10098  *
10099  * Originally Released Under LGPL - original licence link has changed is not relivant.
10100  *
10101  * Fork - LGPL
10102  * <script type="text/javascript">
10103  */
10104
10105
10106
10107 //Notifies Element that fx methods are available
10108 Roo.enableFx = true;
10109
10110 /**
10111  * @class Roo.Fx
10112  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10113  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10114  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10115  * Element effects to work.</p><br/>
10116  *
10117  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10118  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10119  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10120  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10121  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10122  * expected results and should be done with care.</p><br/>
10123  *
10124  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10125  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10126 <pre>
10127 Value  Description
10128 -----  -----------------------------
10129 tl     The top left corner
10130 t      The center of the top edge
10131 tr     The top right corner
10132 l      The center of the left edge
10133 r      The center of the right edge
10134 bl     The bottom left corner
10135 b      The center of the bottom edge
10136 br     The bottom right corner
10137 </pre>
10138  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10139  * below are common options that can be passed to any Fx method.</b>
10140  * @cfg {Function} callback A function called when the effect is finished
10141  * @cfg {Object} scope The scope of the effect function
10142  * @cfg {String} easing A valid Easing value for the effect
10143  * @cfg {String} afterCls A css class to apply after the effect
10144  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10145  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10146  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10147  * effects that end with the element being visually hidden, ignored otherwise)
10148  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10149  * a function which returns such a specification that will be applied to the Element after the effect finishes
10150  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10151  * @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
10152  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10153  */
10154 Roo.Fx = {
10155         /**
10156          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10157          * origin for the slide effect.  This function automatically handles wrapping the element with
10158          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10159          * Usage:
10160          *<pre><code>
10161 // default: slide the element in from the top
10162 el.slideIn();
10163
10164 // custom: slide the element in from the right with a 2-second duration
10165 el.slideIn('r', { duration: 2 });
10166
10167 // common config options shown with default values
10168 el.slideIn('t', {
10169     easing: 'easeOut',
10170     duration: .5
10171 });
10172 </code></pre>
10173          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10174          * @param {Object} options (optional) Object literal with any of the Fx config options
10175          * @return {Roo.Element} The Element
10176          */
10177     slideIn : function(anchor, o){
10178         var el = this.getFxEl();
10179         o = o || {};
10180
10181         el.queueFx(o, function(){
10182
10183             anchor = anchor || "t";
10184
10185             // fix display to visibility
10186             this.fixDisplay();
10187
10188             // restore values after effect
10189             var r = this.getFxRestore();
10190             var b = this.getBox();
10191             // fixed size for slide
10192             this.setSize(b);
10193
10194             // wrap if needed
10195             var wrap = this.fxWrap(r.pos, o, "hidden");
10196
10197             var st = this.dom.style;
10198             st.visibility = "visible";
10199             st.position = "absolute";
10200
10201             // clear out temp styles after slide and unwrap
10202             var after = function(){
10203                 el.fxUnwrap(wrap, r.pos, o);
10204                 st.width = r.width;
10205                 st.height = r.height;
10206                 el.afterFx(o);
10207             };
10208             // time to calc the positions
10209             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10210
10211             switch(anchor.toLowerCase()){
10212                 case "t":
10213                     wrap.setSize(b.width, 0);
10214                     st.left = st.bottom = "0";
10215                     a = {height: bh};
10216                 break;
10217                 case "l":
10218                     wrap.setSize(0, b.height);
10219                     st.right = st.top = "0";
10220                     a = {width: bw};
10221                 break;
10222                 case "r":
10223                     wrap.setSize(0, b.height);
10224                     wrap.setX(b.right);
10225                     st.left = st.top = "0";
10226                     a = {width: bw, points: pt};
10227                 break;
10228                 case "b":
10229                     wrap.setSize(b.width, 0);
10230                     wrap.setY(b.bottom);
10231                     st.left = st.top = "0";
10232                     a = {height: bh, points: pt};
10233                 break;
10234                 case "tl":
10235                     wrap.setSize(0, 0);
10236                     st.right = st.bottom = "0";
10237                     a = {width: bw, height: bh};
10238                 break;
10239                 case "bl":
10240                     wrap.setSize(0, 0);
10241                     wrap.setY(b.y+b.height);
10242                     st.right = st.top = "0";
10243                     a = {width: bw, height: bh, points: pt};
10244                 break;
10245                 case "br":
10246                     wrap.setSize(0, 0);
10247                     wrap.setXY([b.right, b.bottom]);
10248                     st.left = st.top = "0";
10249                     a = {width: bw, height: bh, points: pt};
10250                 break;
10251                 case "tr":
10252                     wrap.setSize(0, 0);
10253                     wrap.setX(b.x+b.width);
10254                     st.left = st.bottom = "0";
10255                     a = {width: bw, height: bh, points: pt};
10256                 break;
10257             }
10258             this.dom.style.visibility = "visible";
10259             wrap.show();
10260
10261             arguments.callee.anim = wrap.fxanim(a,
10262                 o,
10263                 'motion',
10264                 .5,
10265                 'easeOut', after);
10266         });
10267         return this;
10268     },
10269     
10270         /**
10271          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10272          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10273          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10274          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10275          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10276          * Usage:
10277          *<pre><code>
10278 // default: slide the element out to the top
10279 el.slideOut();
10280
10281 // custom: slide the element out to the right with a 2-second duration
10282 el.slideOut('r', { duration: 2 });
10283
10284 // common config options shown with default values
10285 el.slideOut('t', {
10286     easing: 'easeOut',
10287     duration: .5,
10288     remove: false,
10289     useDisplay: false
10290 });
10291 </code></pre>
10292          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10293          * @param {Object} options (optional) Object literal with any of the Fx config options
10294          * @return {Roo.Element} The Element
10295          */
10296     slideOut : function(anchor, o){
10297         var el = this.getFxEl();
10298         o = o || {};
10299
10300         el.queueFx(o, function(){
10301
10302             anchor = anchor || "t";
10303
10304             // restore values after effect
10305             var r = this.getFxRestore();
10306             
10307             var b = this.getBox();
10308             // fixed size for slide
10309             this.setSize(b);
10310
10311             // wrap if needed
10312             var wrap = this.fxWrap(r.pos, o, "visible");
10313
10314             var st = this.dom.style;
10315             st.visibility = "visible";
10316             st.position = "absolute";
10317
10318             wrap.setSize(b);
10319
10320             var after = function(){
10321                 if(o.useDisplay){
10322                     el.setDisplayed(false);
10323                 }else{
10324                     el.hide();
10325                 }
10326
10327                 el.fxUnwrap(wrap, r.pos, o);
10328
10329                 st.width = r.width;
10330                 st.height = r.height;
10331
10332                 el.afterFx(o);
10333             };
10334
10335             var a, zero = {to: 0};
10336             switch(anchor.toLowerCase()){
10337                 case "t":
10338                     st.left = st.bottom = "0";
10339                     a = {height: zero};
10340                 break;
10341                 case "l":
10342                     st.right = st.top = "0";
10343                     a = {width: zero};
10344                 break;
10345                 case "r":
10346                     st.left = st.top = "0";
10347                     a = {width: zero, points: {to:[b.right, b.y]}};
10348                 break;
10349                 case "b":
10350                     st.left = st.top = "0";
10351                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10352                 break;
10353                 case "tl":
10354                     st.right = st.bottom = "0";
10355                     a = {width: zero, height: zero};
10356                 break;
10357                 case "bl":
10358                     st.right = st.top = "0";
10359                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10360                 break;
10361                 case "br":
10362                     st.left = st.top = "0";
10363                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10364                 break;
10365                 case "tr":
10366                     st.left = st.bottom = "0";
10367                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10368                 break;
10369             }
10370
10371             arguments.callee.anim = wrap.fxanim(a,
10372                 o,
10373                 'motion',
10374                 .5,
10375                 "easeOut", after);
10376         });
10377         return this;
10378     },
10379
10380         /**
10381          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10382          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10383          * The element must be removed from the DOM using the 'remove' config option if desired.
10384          * Usage:
10385          *<pre><code>
10386 // default
10387 el.puff();
10388
10389 // common config options shown with default values
10390 el.puff({
10391     easing: 'easeOut',
10392     duration: .5,
10393     remove: false,
10394     useDisplay: false
10395 });
10396 </code></pre>
10397          * @param {Object} options (optional) Object literal with any of the Fx config options
10398          * @return {Roo.Element} The Element
10399          */
10400     puff : function(o){
10401         var el = this.getFxEl();
10402         o = o || {};
10403
10404         el.queueFx(o, function(){
10405             this.clearOpacity();
10406             this.show();
10407
10408             // restore values after effect
10409             var r = this.getFxRestore();
10410             var st = this.dom.style;
10411
10412             var after = function(){
10413                 if(o.useDisplay){
10414                     el.setDisplayed(false);
10415                 }else{
10416                     el.hide();
10417                 }
10418
10419                 el.clearOpacity();
10420
10421                 el.setPositioning(r.pos);
10422                 st.width = r.width;
10423                 st.height = r.height;
10424                 st.fontSize = '';
10425                 el.afterFx(o);
10426             };
10427
10428             var width = this.getWidth();
10429             var height = this.getHeight();
10430
10431             arguments.callee.anim = this.fxanim({
10432                     width : {to: this.adjustWidth(width * 2)},
10433                     height : {to: this.adjustHeight(height * 2)},
10434                     points : {by: [-(width * .5), -(height * .5)]},
10435                     opacity : {to: 0},
10436                     fontSize: {to:200, unit: "%"}
10437                 },
10438                 o,
10439                 'motion',
10440                 .5,
10441                 "easeOut", after);
10442         });
10443         return this;
10444     },
10445
10446         /**
10447          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10448          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10449          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10450          * Usage:
10451          *<pre><code>
10452 // default
10453 el.switchOff();
10454
10455 // all config options shown with default values
10456 el.switchOff({
10457     easing: 'easeIn',
10458     duration: .3,
10459     remove: false,
10460     useDisplay: false
10461 });
10462 </code></pre>
10463          * @param {Object} options (optional) Object literal with any of the Fx config options
10464          * @return {Roo.Element} The Element
10465          */
10466     switchOff : function(o){
10467         var el = this.getFxEl();
10468         o = o || {};
10469
10470         el.queueFx(o, function(){
10471             this.clearOpacity();
10472             this.clip();
10473
10474             // restore values after effect
10475             var r = this.getFxRestore();
10476             var st = this.dom.style;
10477
10478             var after = function(){
10479                 if(o.useDisplay){
10480                     el.setDisplayed(false);
10481                 }else{
10482                     el.hide();
10483                 }
10484
10485                 el.clearOpacity();
10486                 el.setPositioning(r.pos);
10487                 st.width = r.width;
10488                 st.height = r.height;
10489
10490                 el.afterFx(o);
10491             };
10492
10493             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10494                 this.clearOpacity();
10495                 (function(){
10496                     this.fxanim({
10497                         height:{to:1},
10498                         points:{by:[0, this.getHeight() * .5]}
10499                     }, o, 'motion', 0.3, 'easeIn', after);
10500                 }).defer(100, this);
10501             });
10502         });
10503         return this;
10504     },
10505
10506     /**
10507      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10508      * changed using the "attr" config option) and then fading back to the original color. If no original
10509      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10510      * Usage:
10511 <pre><code>
10512 // default: highlight background to yellow
10513 el.highlight();
10514
10515 // custom: highlight foreground text to blue for 2 seconds
10516 el.highlight("0000ff", { attr: 'color', duration: 2 });
10517
10518 // common config options shown with default values
10519 el.highlight("ffff9c", {
10520     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10521     endColor: (current color) or "ffffff",
10522     easing: 'easeIn',
10523     duration: 1
10524 });
10525 </code></pre>
10526      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10527      * @param {Object} options (optional) Object literal with any of the Fx config options
10528      * @return {Roo.Element} The Element
10529      */ 
10530     highlight : function(color, o){
10531         var el = this.getFxEl();
10532         o = o || {};
10533
10534         el.queueFx(o, function(){
10535             color = color || "ffff9c";
10536             attr = o.attr || "backgroundColor";
10537
10538             this.clearOpacity();
10539             this.show();
10540
10541             var origColor = this.getColor(attr);
10542             var restoreColor = this.dom.style[attr];
10543             endColor = (o.endColor || origColor) || "ffffff";
10544
10545             var after = function(){
10546                 el.dom.style[attr] = restoreColor;
10547                 el.afterFx(o);
10548             };
10549
10550             var a = {};
10551             a[attr] = {from: color, to: endColor};
10552             arguments.callee.anim = this.fxanim(a,
10553                 o,
10554                 'color',
10555                 1,
10556                 'easeIn', after);
10557         });
10558         return this;
10559     },
10560
10561    /**
10562     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10563     * Usage:
10564 <pre><code>
10565 // default: a single light blue ripple
10566 el.frame();
10567
10568 // custom: 3 red ripples lasting 3 seconds total
10569 el.frame("ff0000", 3, { duration: 3 });
10570
10571 // common config options shown with default values
10572 el.frame("C3DAF9", 1, {
10573     duration: 1 //duration of entire animation (not each individual ripple)
10574     // Note: Easing is not configurable and will be ignored if included
10575 });
10576 </code></pre>
10577     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10578     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10579     * @param {Object} options (optional) Object literal with any of the Fx config options
10580     * @return {Roo.Element} The Element
10581     */
10582     frame : function(color, count, o){
10583         var el = this.getFxEl();
10584         o = o || {};
10585
10586         el.queueFx(o, function(){
10587             color = color || "#C3DAF9";
10588             if(color.length == 6){
10589                 color = "#" + color;
10590             }
10591             count = count || 1;
10592             duration = o.duration || 1;
10593             this.show();
10594
10595             var b = this.getBox();
10596             var animFn = function(){
10597                 var proxy = this.createProxy({
10598
10599                      style:{
10600                         visbility:"hidden",
10601                         position:"absolute",
10602                         "z-index":"35000", // yee haw
10603                         border:"0px solid " + color
10604                      }
10605                   });
10606                 var scale = Roo.isBorderBox ? 2 : 1;
10607                 proxy.animate({
10608                     top:{from:b.y, to:b.y - 20},
10609                     left:{from:b.x, to:b.x - 20},
10610                     borderWidth:{from:0, to:10},
10611                     opacity:{from:1, to:0},
10612                     height:{from:b.height, to:(b.height + (20*scale))},
10613                     width:{from:b.width, to:(b.width + (20*scale))}
10614                 }, duration, function(){
10615                     proxy.remove();
10616                 });
10617                 if(--count > 0){
10618                      animFn.defer((duration/2)*1000, this);
10619                 }else{
10620                     el.afterFx(o);
10621                 }
10622             };
10623             animFn.call(this);
10624         });
10625         return this;
10626     },
10627
10628    /**
10629     * Creates a pause before any subsequent queued effects begin.  If there are
10630     * no effects queued after the pause it will have no effect.
10631     * Usage:
10632 <pre><code>
10633 el.pause(1);
10634 </code></pre>
10635     * @param {Number} seconds The length of time to pause (in seconds)
10636     * @return {Roo.Element} The Element
10637     */
10638     pause : function(seconds){
10639         var el = this.getFxEl();
10640         var o = {};
10641
10642         el.queueFx(o, function(){
10643             setTimeout(function(){
10644                 el.afterFx(o);
10645             }, seconds * 1000);
10646         });
10647         return this;
10648     },
10649
10650    /**
10651     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10652     * using the "endOpacity" config option.
10653     * Usage:
10654 <pre><code>
10655 // default: fade in from opacity 0 to 100%
10656 el.fadeIn();
10657
10658 // custom: fade in from opacity 0 to 75% over 2 seconds
10659 el.fadeIn({ endOpacity: .75, duration: 2});
10660
10661 // common config options shown with default values
10662 el.fadeIn({
10663     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10664     easing: 'easeOut',
10665     duration: .5
10666 });
10667 </code></pre>
10668     * @param {Object} options (optional) Object literal with any of the Fx config options
10669     * @return {Roo.Element} The Element
10670     */
10671     fadeIn : function(o){
10672         var el = this.getFxEl();
10673         o = o || {};
10674         el.queueFx(o, function(){
10675             this.setOpacity(0);
10676             this.fixDisplay();
10677             this.dom.style.visibility = 'visible';
10678             var to = o.endOpacity || 1;
10679             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10680                 o, null, .5, "easeOut", function(){
10681                 if(to == 1){
10682                     this.clearOpacity();
10683                 }
10684                 el.afterFx(o);
10685             });
10686         });
10687         return this;
10688     },
10689
10690    /**
10691     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10692     * using the "endOpacity" config option.
10693     * Usage:
10694 <pre><code>
10695 // default: fade out from the element's current opacity to 0
10696 el.fadeOut();
10697
10698 // custom: fade out from the element's current opacity to 25% over 2 seconds
10699 el.fadeOut({ endOpacity: .25, duration: 2});
10700
10701 // common config options shown with default values
10702 el.fadeOut({
10703     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10704     easing: 'easeOut',
10705     duration: .5
10706     remove: false,
10707     useDisplay: false
10708 });
10709 </code></pre>
10710     * @param {Object} options (optional) Object literal with any of the Fx config options
10711     * @return {Roo.Element} The Element
10712     */
10713     fadeOut : function(o){
10714         var el = this.getFxEl();
10715         o = o || {};
10716         el.queueFx(o, function(){
10717             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10718                 o, null, .5, "easeOut", function(){
10719                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10720                      this.dom.style.display = "none";
10721                 }else{
10722                      this.dom.style.visibility = "hidden";
10723                 }
10724                 this.clearOpacity();
10725                 el.afterFx(o);
10726             });
10727         });
10728         return this;
10729     },
10730
10731    /**
10732     * Animates the transition of an element's dimensions from a starting height/width
10733     * to an ending height/width.
10734     * Usage:
10735 <pre><code>
10736 // change height and width to 100x100 pixels
10737 el.scale(100, 100);
10738
10739 // common config options shown with default values.  The height and width will default to
10740 // the element's existing values if passed as null.
10741 el.scale(
10742     [element's width],
10743     [element's height], {
10744     easing: 'easeOut',
10745     duration: .35
10746 });
10747 </code></pre>
10748     * @param {Number} width  The new width (pass undefined to keep the original width)
10749     * @param {Number} height  The new height (pass undefined to keep the original height)
10750     * @param {Object} options (optional) Object literal with any of the Fx config options
10751     * @return {Roo.Element} The Element
10752     */
10753     scale : function(w, h, o){
10754         this.shift(Roo.apply({}, o, {
10755             width: w,
10756             height: h
10757         }));
10758         return this;
10759     },
10760
10761    /**
10762     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10763     * Any of these properties not specified in the config object will not be changed.  This effect 
10764     * requires that at least one new dimension, position or opacity setting must be passed in on
10765     * the config object in order for the function to have any effect.
10766     * Usage:
10767 <pre><code>
10768 // slide the element horizontally to x position 200 while changing the height and opacity
10769 el.shift({ x: 200, height: 50, opacity: .8 });
10770
10771 // common config options shown with default values.
10772 el.shift({
10773     width: [element's width],
10774     height: [element's height],
10775     x: [element's x position],
10776     y: [element's y position],
10777     opacity: [element's opacity],
10778     easing: 'easeOut',
10779     duration: .35
10780 });
10781 </code></pre>
10782     * @param {Object} options  Object literal with any of the Fx config options
10783     * @return {Roo.Element} The Element
10784     */
10785     shift : function(o){
10786         var el = this.getFxEl();
10787         o = o || {};
10788         el.queueFx(o, function(){
10789             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10790             if(w !== undefined){
10791                 a.width = {to: this.adjustWidth(w)};
10792             }
10793             if(h !== undefined){
10794                 a.height = {to: this.adjustHeight(h)};
10795             }
10796             if(x !== undefined || y !== undefined){
10797                 a.points = {to: [
10798                     x !== undefined ? x : this.getX(),
10799                     y !== undefined ? y : this.getY()
10800                 ]};
10801             }
10802             if(op !== undefined){
10803                 a.opacity = {to: op};
10804             }
10805             if(o.xy !== undefined){
10806                 a.points = {to: o.xy};
10807             }
10808             arguments.callee.anim = this.fxanim(a,
10809                 o, 'motion', .35, "easeOut", function(){
10810                 el.afterFx(o);
10811             });
10812         });
10813         return this;
10814     },
10815
10816         /**
10817          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10818          * ending point of the effect.
10819          * Usage:
10820          *<pre><code>
10821 // default: slide the element downward while fading out
10822 el.ghost();
10823
10824 // custom: slide the element out to the right with a 2-second duration
10825 el.ghost('r', { duration: 2 });
10826
10827 // common config options shown with default values
10828 el.ghost('b', {
10829     easing: 'easeOut',
10830     duration: .5
10831     remove: false,
10832     useDisplay: false
10833 });
10834 </code></pre>
10835          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10836          * @param {Object} options (optional) Object literal with any of the Fx config options
10837          * @return {Roo.Element} The Element
10838          */
10839     ghost : function(anchor, o){
10840         var el = this.getFxEl();
10841         o = o || {};
10842
10843         el.queueFx(o, function(){
10844             anchor = anchor || "b";
10845
10846             // restore values after effect
10847             var r = this.getFxRestore();
10848             var w = this.getWidth(),
10849                 h = this.getHeight();
10850
10851             var st = this.dom.style;
10852
10853             var after = function(){
10854                 if(o.useDisplay){
10855                     el.setDisplayed(false);
10856                 }else{
10857                     el.hide();
10858                 }
10859
10860                 el.clearOpacity();
10861                 el.setPositioning(r.pos);
10862                 st.width = r.width;
10863                 st.height = r.height;
10864
10865                 el.afterFx(o);
10866             };
10867
10868             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10869             switch(anchor.toLowerCase()){
10870                 case "t":
10871                     pt.by = [0, -h];
10872                 break;
10873                 case "l":
10874                     pt.by = [-w, 0];
10875                 break;
10876                 case "r":
10877                     pt.by = [w, 0];
10878                 break;
10879                 case "b":
10880                     pt.by = [0, h];
10881                 break;
10882                 case "tl":
10883                     pt.by = [-w, -h];
10884                 break;
10885                 case "bl":
10886                     pt.by = [-w, h];
10887                 break;
10888                 case "br":
10889                     pt.by = [w, h];
10890                 break;
10891                 case "tr":
10892                     pt.by = [w, -h];
10893                 break;
10894             }
10895
10896             arguments.callee.anim = this.fxanim(a,
10897                 o,
10898                 'motion',
10899                 .5,
10900                 "easeOut", after);
10901         });
10902         return this;
10903     },
10904
10905         /**
10906          * Ensures that all effects queued after syncFx is called on the element are
10907          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10908          * @return {Roo.Element} The Element
10909          */
10910     syncFx : function(){
10911         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10912             block : false,
10913             concurrent : true,
10914             stopFx : false
10915         });
10916         return this;
10917     },
10918
10919         /**
10920          * Ensures that all effects queued after sequenceFx is called on the element are
10921          * run in sequence.  This is the opposite of {@link #syncFx}.
10922          * @return {Roo.Element} The Element
10923          */
10924     sequenceFx : function(){
10925         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10926             block : false,
10927             concurrent : false,
10928             stopFx : false
10929         });
10930         return this;
10931     },
10932
10933         /* @private */
10934     nextFx : function(){
10935         var ef = this.fxQueue[0];
10936         if(ef){
10937             ef.call(this);
10938         }
10939     },
10940
10941         /**
10942          * Returns true if the element has any effects actively running or queued, else returns false.
10943          * @return {Boolean} True if element has active effects, else false
10944          */
10945     hasActiveFx : function(){
10946         return this.fxQueue && this.fxQueue[0];
10947     },
10948
10949         /**
10950          * Stops any running effects and clears the element's internal effects queue if it contains
10951          * any additional effects that haven't started yet.
10952          * @return {Roo.Element} The Element
10953          */
10954     stopFx : function(){
10955         if(this.hasActiveFx()){
10956             var cur = this.fxQueue[0];
10957             if(cur && cur.anim && cur.anim.isAnimated()){
10958                 this.fxQueue = [cur]; // clear out others
10959                 cur.anim.stop(true);
10960             }
10961         }
10962         return this;
10963     },
10964
10965         /* @private */
10966     beforeFx : function(o){
10967         if(this.hasActiveFx() && !o.concurrent){
10968            if(o.stopFx){
10969                this.stopFx();
10970                return true;
10971            }
10972            return false;
10973         }
10974         return true;
10975     },
10976
10977         /**
10978          * Returns true if the element is currently blocking so that no other effect can be queued
10979          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10980          * used to ensure that an effect initiated by a user action runs to completion prior to the
10981          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10982          * @return {Boolean} True if blocking, else false
10983          */
10984     hasFxBlock : function(){
10985         var q = this.fxQueue;
10986         return q && q[0] && q[0].block;
10987     },
10988
10989         /* @private */
10990     queueFx : function(o, fn){
10991         if(!this.fxQueue){
10992             this.fxQueue = [];
10993         }
10994         if(!this.hasFxBlock()){
10995             Roo.applyIf(o, this.fxDefaults);
10996             if(!o.concurrent){
10997                 var run = this.beforeFx(o);
10998                 fn.block = o.block;
10999                 this.fxQueue.push(fn);
11000                 if(run){
11001                     this.nextFx();
11002                 }
11003             }else{
11004                 fn.call(this);
11005             }
11006         }
11007         return this;
11008     },
11009
11010         /* @private */
11011     fxWrap : function(pos, o, vis){
11012         var wrap;
11013         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11014             var wrapXY;
11015             if(o.fixPosition){
11016                 wrapXY = this.getXY();
11017             }
11018             var div = document.createElement("div");
11019             div.style.visibility = vis;
11020             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11021             wrap.setPositioning(pos);
11022             if(wrap.getStyle("position") == "static"){
11023                 wrap.position("relative");
11024             }
11025             this.clearPositioning('auto');
11026             wrap.clip();
11027             wrap.dom.appendChild(this.dom);
11028             if(wrapXY){
11029                 wrap.setXY(wrapXY);
11030             }
11031         }
11032         return wrap;
11033     },
11034
11035         /* @private */
11036     fxUnwrap : function(wrap, pos, o){
11037         this.clearPositioning();
11038         this.setPositioning(pos);
11039         if(!o.wrap){
11040             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11041             wrap.remove();
11042         }
11043     },
11044
11045         /* @private */
11046     getFxRestore : function(){
11047         var st = this.dom.style;
11048         return {pos: this.getPositioning(), width: st.width, height : st.height};
11049     },
11050
11051         /* @private */
11052     afterFx : function(o){
11053         if(o.afterStyle){
11054             this.applyStyles(o.afterStyle);
11055         }
11056         if(o.afterCls){
11057             this.addClass(o.afterCls);
11058         }
11059         if(o.remove === true){
11060             this.remove();
11061         }
11062         Roo.callback(o.callback, o.scope, [this]);
11063         if(!o.concurrent){
11064             this.fxQueue.shift();
11065             this.nextFx();
11066         }
11067     },
11068
11069         /* @private */
11070     getFxEl : function(){ // support for composite element fx
11071         return Roo.get(this.dom);
11072     },
11073
11074         /* @private */
11075     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11076         animType = animType || 'run';
11077         opt = opt || {};
11078         var anim = Roo.lib.Anim[animType](
11079             this.dom, args,
11080             (opt.duration || defaultDur) || .35,
11081             (opt.easing || defaultEase) || 'easeOut',
11082             function(){
11083                 Roo.callback(cb, this);
11084             },
11085             this
11086         );
11087         opt.anim = anim;
11088         return anim;
11089     }
11090 };
11091
11092 // backwords compat
11093 Roo.Fx.resize = Roo.Fx.scale;
11094
11095 //When included, Roo.Fx is automatically applied to Element so that all basic
11096 //effects are available directly via the Element API
11097 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11098  * Based on:
11099  * Ext JS Library 1.1.1
11100  * Copyright(c) 2006-2007, Ext JS, LLC.
11101  *
11102  * Originally Released Under LGPL - original licence link has changed is not relivant.
11103  *
11104  * Fork - LGPL
11105  * <script type="text/javascript">
11106  */
11107
11108
11109 /**
11110  * @class Roo.CompositeElement
11111  * Standard composite class. Creates a Roo.Element for every element in the collection.
11112  * <br><br>
11113  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11114  * actions will be performed on all the elements in this collection.</b>
11115  * <br><br>
11116  * All methods return <i>this</i> and can be chained.
11117  <pre><code>
11118  var els = Roo.select("#some-el div.some-class", true);
11119  // or select directly from an existing element
11120  var el = Roo.get('some-el');
11121  el.select('div.some-class', true);
11122
11123  els.setWidth(100); // all elements become 100 width
11124  els.hide(true); // all elements fade out and hide
11125  // or
11126  els.setWidth(100).hide(true);
11127  </code></pre>
11128  */
11129 Roo.CompositeElement = function(els){
11130     this.elements = [];
11131     this.addElements(els);
11132 };
11133 Roo.CompositeElement.prototype = {
11134     isComposite: true,
11135     addElements : function(els){
11136         if(!els) {
11137             return this;
11138         }
11139         if(typeof els == "string"){
11140             els = Roo.Element.selectorFunction(els);
11141         }
11142         var yels = this.elements;
11143         var index = yels.length-1;
11144         for(var i = 0, len = els.length; i < len; i++) {
11145                 yels[++index] = Roo.get(els[i]);
11146         }
11147         return this;
11148     },
11149
11150     /**
11151     * Clears this composite and adds the elements returned by the passed selector.
11152     * @param {String/Array} els A string CSS selector, an array of elements or an element
11153     * @return {CompositeElement} this
11154     */
11155     fill : function(els){
11156         this.elements = [];
11157         this.add(els);
11158         return this;
11159     },
11160
11161     /**
11162     * Filters this composite to only elements that match the passed selector.
11163     * @param {String} selector A string CSS selector
11164     * @param {Boolean} inverse return inverse filter (not matches)
11165     * @return {CompositeElement} this
11166     */
11167     filter : function(selector, inverse){
11168         var els = [];
11169         inverse = inverse || false;
11170         this.each(function(el){
11171             var match = inverse ? !el.is(selector) : el.is(selector);
11172             if(match){
11173                 els[els.length] = el.dom;
11174             }
11175         });
11176         this.fill(els);
11177         return this;
11178     },
11179
11180     invoke : function(fn, args){
11181         var els = this.elements;
11182         for(var i = 0, len = els.length; i < len; i++) {
11183                 Roo.Element.prototype[fn].apply(els[i], args);
11184         }
11185         return this;
11186     },
11187     /**
11188     * Adds elements to this composite.
11189     * @param {String/Array} els A string CSS selector, an array of elements or an element
11190     * @return {CompositeElement} this
11191     */
11192     add : function(els){
11193         if(typeof els == "string"){
11194             this.addElements(Roo.Element.selectorFunction(els));
11195         }else if(els.length !== undefined){
11196             this.addElements(els);
11197         }else{
11198             this.addElements([els]);
11199         }
11200         return this;
11201     },
11202     /**
11203     * Calls the passed function passing (el, this, index) for each element in this composite.
11204     * @param {Function} fn The function to call
11205     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11206     * @return {CompositeElement} this
11207     */
11208     each : function(fn, scope){
11209         var els = this.elements;
11210         for(var i = 0, len = els.length; i < len; i++){
11211             if(fn.call(scope || els[i], els[i], this, i) === false) {
11212                 break;
11213             }
11214         }
11215         return this;
11216     },
11217
11218     /**
11219      * Returns the Element object at the specified index
11220      * @param {Number} index
11221      * @return {Roo.Element}
11222      */
11223     item : function(index){
11224         return this.elements[index] || null;
11225     },
11226
11227     /**
11228      * Returns the first Element
11229      * @return {Roo.Element}
11230      */
11231     first : function(){
11232         return this.item(0);
11233     },
11234
11235     /**
11236      * Returns the last Element
11237      * @return {Roo.Element}
11238      */
11239     last : function(){
11240         return this.item(this.elements.length-1);
11241     },
11242
11243     /**
11244      * Returns the number of elements in this composite
11245      * @return Number
11246      */
11247     getCount : function(){
11248         return this.elements.length;
11249     },
11250
11251     /**
11252      * Returns true if this composite contains the passed element
11253      * @return Boolean
11254      */
11255     contains : function(el){
11256         return this.indexOf(el) !== -1;
11257     },
11258
11259     /**
11260      * Returns true if this composite contains the passed element
11261      * @return Boolean
11262      */
11263     indexOf : function(el){
11264         return this.elements.indexOf(Roo.get(el));
11265     },
11266
11267
11268     /**
11269     * Removes the specified element(s).
11270     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11271     * or an array of any of those.
11272     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11273     * @return {CompositeElement} this
11274     */
11275     removeElement : function(el, removeDom){
11276         if(el instanceof Array){
11277             for(var i = 0, len = el.length; i < len; i++){
11278                 this.removeElement(el[i]);
11279             }
11280             return this;
11281         }
11282         var index = typeof el == 'number' ? el : this.indexOf(el);
11283         if(index !== -1){
11284             if(removeDom){
11285                 var d = this.elements[index];
11286                 if(d.dom){
11287                     d.remove();
11288                 }else{
11289                     d.parentNode.removeChild(d);
11290                 }
11291             }
11292             this.elements.splice(index, 1);
11293         }
11294         return this;
11295     },
11296
11297     /**
11298     * Replaces the specified element with the passed element.
11299     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11300     * to replace.
11301     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11302     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11303     * @return {CompositeElement} this
11304     */
11305     replaceElement : function(el, replacement, domReplace){
11306         var index = typeof el == 'number' ? el : this.indexOf(el);
11307         if(index !== -1){
11308             if(domReplace){
11309                 this.elements[index].replaceWith(replacement);
11310             }else{
11311                 this.elements.splice(index, 1, Roo.get(replacement))
11312             }
11313         }
11314         return this;
11315     },
11316
11317     /**
11318      * Removes all elements.
11319      */
11320     clear : function(){
11321         this.elements = [];
11322     }
11323 };
11324 (function(){
11325     Roo.CompositeElement.createCall = function(proto, fnName){
11326         if(!proto[fnName]){
11327             proto[fnName] = function(){
11328                 return this.invoke(fnName, arguments);
11329             };
11330         }
11331     };
11332     for(var fnName in Roo.Element.prototype){
11333         if(typeof Roo.Element.prototype[fnName] == "function"){
11334             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11335         }
11336     };
11337 })();
11338 /*
11339  * Based on:
11340  * Ext JS Library 1.1.1
11341  * Copyright(c) 2006-2007, Ext JS, LLC.
11342  *
11343  * Originally Released Under LGPL - original licence link has changed is not relivant.
11344  *
11345  * Fork - LGPL
11346  * <script type="text/javascript">
11347  */
11348
11349 /**
11350  * @class Roo.CompositeElementLite
11351  * @extends Roo.CompositeElement
11352  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11353  <pre><code>
11354  var els = Roo.select("#some-el div.some-class");
11355  // or select directly from an existing element
11356  var el = Roo.get('some-el');
11357  el.select('div.some-class');
11358
11359  els.setWidth(100); // all elements become 100 width
11360  els.hide(true); // all elements fade out and hide
11361  // or
11362  els.setWidth(100).hide(true);
11363  </code></pre><br><br>
11364  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11365  * actions will be performed on all the elements in this collection.</b>
11366  */
11367 Roo.CompositeElementLite = function(els){
11368     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11369     this.el = new Roo.Element.Flyweight();
11370 };
11371 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11372     addElements : function(els){
11373         if(els){
11374             if(els instanceof Array){
11375                 this.elements = this.elements.concat(els);
11376             }else{
11377                 var yels = this.elements;
11378                 var index = yels.length-1;
11379                 for(var i = 0, len = els.length; i < len; i++) {
11380                     yels[++index] = els[i];
11381                 }
11382             }
11383         }
11384         return this;
11385     },
11386     invoke : function(fn, args){
11387         var els = this.elements;
11388         var el = this.el;
11389         for(var i = 0, len = els.length; i < len; i++) {
11390             el.dom = els[i];
11391                 Roo.Element.prototype[fn].apply(el, args);
11392         }
11393         return this;
11394     },
11395     /**
11396      * Returns a flyweight Element of the dom element object at the specified index
11397      * @param {Number} index
11398      * @return {Roo.Element}
11399      */
11400     item : function(index){
11401         if(!this.elements[index]){
11402             return null;
11403         }
11404         this.el.dom = this.elements[index];
11405         return this.el;
11406     },
11407
11408     // fixes scope with flyweight
11409     addListener : function(eventName, handler, scope, opt){
11410         var els = this.elements;
11411         for(var i = 0, len = els.length; i < len; i++) {
11412             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11413         }
11414         return this;
11415     },
11416
11417     /**
11418     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11419     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11420     * a reference to the dom node, use el.dom.</b>
11421     * @param {Function} fn The function to call
11422     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11423     * @return {CompositeElement} this
11424     */
11425     each : function(fn, scope){
11426         var els = this.elements;
11427         var el = this.el;
11428         for(var i = 0, len = els.length; i < len; i++){
11429             el.dom = els[i];
11430                 if(fn.call(scope || el, el, this, i) === false){
11431                 break;
11432             }
11433         }
11434         return this;
11435     },
11436
11437     indexOf : function(el){
11438         return this.elements.indexOf(Roo.getDom(el));
11439     },
11440
11441     replaceElement : function(el, replacement, domReplace){
11442         var index = typeof el == 'number' ? el : this.indexOf(el);
11443         if(index !== -1){
11444             replacement = Roo.getDom(replacement);
11445             if(domReplace){
11446                 var d = this.elements[index];
11447                 d.parentNode.insertBefore(replacement, d);
11448                 d.parentNode.removeChild(d);
11449             }
11450             this.elements.splice(index, 1, replacement);
11451         }
11452         return this;
11453     }
11454 });
11455 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11456
11457 /*
11458  * Based on:
11459  * Ext JS Library 1.1.1
11460  * Copyright(c) 2006-2007, Ext JS, LLC.
11461  *
11462  * Originally Released Under LGPL - original licence link has changed is not relivant.
11463  *
11464  * Fork - LGPL
11465  * <script type="text/javascript">
11466  */
11467
11468  
11469
11470 /**
11471  * @class Roo.data.Connection
11472  * @extends Roo.util.Observable
11473  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11474  * either to a configured URL, or to a URL specified at request time.<br><br>
11475  * <p>
11476  * Requests made by this class are asynchronous, and will return immediately. No data from
11477  * the server will be available to the statement immediately following the {@link #request} call.
11478  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11479  * <p>
11480  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11481  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11482  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11483  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11484  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11485  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11486  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11487  * standard DOM methods.
11488  * @constructor
11489  * @param {Object} config a configuration object.
11490  */
11491 Roo.data.Connection = function(config){
11492     Roo.apply(this, config);
11493     this.addEvents({
11494         /**
11495          * @event beforerequest
11496          * Fires before a network request is made to retrieve a data object.
11497          * @param {Connection} conn This Connection object.
11498          * @param {Object} options The options config object passed to the {@link #request} method.
11499          */
11500         "beforerequest" : true,
11501         /**
11502          * @event requestcomplete
11503          * Fires if the request was successfully completed.
11504          * @param {Connection} conn This Connection object.
11505          * @param {Object} response The XHR object containing the response data.
11506          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11507          * @param {Object} options The options config object passed to the {@link #request} method.
11508          */
11509         "requestcomplete" : true,
11510         /**
11511          * @event requestexception
11512          * Fires if an error HTTP status was returned from the server.
11513          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11514          * @param {Connection} conn This Connection object.
11515          * @param {Object} response The XHR object containing the response data.
11516          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11517          * @param {Object} options The options config object passed to the {@link #request} method.
11518          */
11519         "requestexception" : true
11520     });
11521     Roo.data.Connection.superclass.constructor.call(this);
11522 };
11523
11524 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11525     /**
11526      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11527      */
11528     /**
11529      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11530      * extra parameters to each request made by this object. (defaults to undefined)
11531      */
11532     /**
11533      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11534      *  to each request made by this object. (defaults to undefined)
11535      */
11536     /**
11537      * @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)
11538      */
11539     /**
11540      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11541      */
11542     timeout : 30000,
11543     /**
11544      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11545      * @type Boolean
11546      */
11547     autoAbort:false,
11548
11549     /**
11550      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11551      * @type Boolean
11552      */
11553     disableCaching: true,
11554
11555     /**
11556      * Sends an HTTP request to a remote server.
11557      * @param {Object} options An object which may contain the following properties:<ul>
11558      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11559      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11560      * request, a url encoded string or a function to call to get either.</li>
11561      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11562      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11563      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11564      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11565      * <li>options {Object} The parameter to the request call.</li>
11566      * <li>success {Boolean} True if the request succeeded.</li>
11567      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11568      * </ul></li>
11569      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11570      * The callback is passed the following parameters:<ul>
11571      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11572      * <li>options {Object} The parameter to the request call.</li>
11573      * </ul></li>
11574      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11575      * The callback is passed the following parameters:<ul>
11576      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11577      * <li>options {Object} The parameter to the request call.</li>
11578      * </ul></li>
11579      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11580      * for the callback function. Defaults to the browser window.</li>
11581      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11582      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11583      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11584      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11585      * params for the post data. Any params will be appended to the URL.</li>
11586      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11587      * </ul>
11588      * @return {Number} transactionId
11589      */
11590     request : function(o){
11591         if(this.fireEvent("beforerequest", this, o) !== false){
11592             var p = o.params;
11593
11594             if(typeof p == "function"){
11595                 p = p.call(o.scope||window, o);
11596             }
11597             if(typeof p == "object"){
11598                 p = Roo.urlEncode(o.params);
11599             }
11600             if(this.extraParams){
11601                 var extras = Roo.urlEncode(this.extraParams);
11602                 p = p ? (p + '&' + extras) : extras;
11603             }
11604
11605             var url = o.url || this.url;
11606             if(typeof url == 'function'){
11607                 url = url.call(o.scope||window, o);
11608             }
11609
11610             if(o.form){
11611                 var form = Roo.getDom(o.form);
11612                 url = url || form.action;
11613
11614                 var enctype = form.getAttribute("enctype");
11615                 
11616                 if (o.formData) {
11617                     return this.doFormDataUpload(o,p,url);
11618                 }
11619                 
11620                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11621                     return this.doFormUpload(o, p, url);
11622                 }
11623                 var f = Roo.lib.Ajax.serializeForm(form);
11624                 p = p ? (p + '&' + f) : f;
11625             }
11626
11627             var hs = o.headers;
11628             if(this.defaultHeaders){
11629                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11630                 if(!o.headers){
11631                     o.headers = hs;
11632                 }
11633             }
11634
11635             var cb = {
11636                 success: this.handleResponse,
11637                 failure: this.handleFailure,
11638                 scope: this,
11639                 argument: {options: o},
11640                 timeout : o.timeout || this.timeout
11641             };
11642
11643             var method = o.method||this.method||(p ? "POST" : "GET");
11644
11645             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11646                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11647             }
11648
11649             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11650                 if(o.autoAbort){
11651                     this.abort();
11652                 }
11653             }else if(this.autoAbort !== false){
11654                 this.abort();
11655             }
11656
11657             if((method == 'GET' && p) || o.xmlData){
11658                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11659                 p = '';
11660             }
11661             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11662             return this.transId;
11663         }else{
11664             Roo.callback(o.callback, o.scope, [o, null, null]);
11665             return null;
11666         }
11667     },
11668
11669     /**
11670      * Determine whether this object has a request outstanding.
11671      * @param {Number} transactionId (Optional) defaults to the last transaction
11672      * @return {Boolean} True if there is an outstanding request.
11673      */
11674     isLoading : function(transId){
11675         if(transId){
11676             return Roo.lib.Ajax.isCallInProgress(transId);
11677         }else{
11678             return this.transId ? true : false;
11679         }
11680     },
11681
11682     /**
11683      * Aborts any outstanding request.
11684      * @param {Number} transactionId (Optional) defaults to the last transaction
11685      */
11686     abort : function(transId){
11687         if(transId || this.isLoading()){
11688             Roo.lib.Ajax.abort(transId || this.transId);
11689         }
11690     },
11691
11692     // private
11693     handleResponse : function(response){
11694         this.transId = false;
11695         var options = response.argument.options;
11696         response.argument = options ? options.argument : null;
11697         this.fireEvent("requestcomplete", this, response, options);
11698         Roo.callback(options.success, options.scope, [response, options]);
11699         Roo.callback(options.callback, options.scope, [options, true, response]);
11700     },
11701
11702     // private
11703     handleFailure : function(response, e){
11704         this.transId = false;
11705         var options = response.argument.options;
11706         response.argument = options ? options.argument : null;
11707         this.fireEvent("requestexception", this, response, options, e);
11708         Roo.callback(options.failure, options.scope, [response, options]);
11709         Roo.callback(options.callback, options.scope, [options, false, response]);
11710     },
11711
11712     // private
11713     doFormUpload : function(o, ps, url){
11714         var id = Roo.id();
11715         var frame = document.createElement('iframe');
11716         frame.id = id;
11717         frame.name = id;
11718         frame.className = 'x-hidden';
11719         if(Roo.isIE){
11720             frame.src = Roo.SSL_SECURE_URL;
11721         }
11722         document.body.appendChild(frame);
11723
11724         if(Roo.isIE){
11725            document.frames[id].name = id;
11726         }
11727
11728         var form = Roo.getDom(o.form);
11729         form.target = id;
11730         form.method = 'POST';
11731         form.enctype = form.encoding = 'multipart/form-data';
11732         if(url){
11733             form.action = url;
11734         }
11735
11736         var hiddens, hd;
11737         if(ps){ // add dynamic params
11738             hiddens = [];
11739             ps = Roo.urlDecode(ps, false);
11740             for(var k in ps){
11741                 if(ps.hasOwnProperty(k)){
11742                     hd = document.createElement('input');
11743                     hd.type = 'hidden';
11744                     hd.name = k;
11745                     hd.value = ps[k];
11746                     form.appendChild(hd);
11747                     hiddens.push(hd);
11748                 }
11749             }
11750         }
11751
11752         function cb(){
11753             var r = {  // bogus response object
11754                 responseText : '',
11755                 responseXML : null
11756             };
11757
11758             r.argument = o ? o.argument : null;
11759
11760             try { //
11761                 var doc;
11762                 if(Roo.isIE){
11763                     doc = frame.contentWindow.document;
11764                 }else {
11765                     doc = (frame.contentDocument || window.frames[id].document);
11766                 }
11767                 if(doc && doc.body){
11768                     r.responseText = doc.body.innerHTML;
11769                 }
11770                 if(doc && doc.XMLDocument){
11771                     r.responseXML = doc.XMLDocument;
11772                 }else {
11773                     r.responseXML = doc;
11774                 }
11775             }
11776             catch(e) {
11777                 // ignore
11778             }
11779
11780             Roo.EventManager.removeListener(frame, 'load', cb, this);
11781
11782             this.fireEvent("requestcomplete", this, r, o);
11783             Roo.callback(o.success, o.scope, [r, o]);
11784             Roo.callback(o.callback, o.scope, [o, true, r]);
11785
11786             setTimeout(function(){document.body.removeChild(frame);}, 100);
11787         }
11788
11789         Roo.EventManager.on(frame, 'load', cb, this);
11790         form.submit();
11791
11792         if(hiddens){ // remove dynamic params
11793             for(var i = 0, len = hiddens.length; i < len; i++){
11794                 form.removeChild(hiddens[i]);
11795             }
11796         }
11797     },
11798     // this is a 'formdata version???'
11799     
11800     
11801     doFormDataUpload : function(o, ps, url)
11802     {
11803         var formData = o.formData === true ? new FormData(Roo.getDom(o.form)) : o.formData;
11804       
11805         var cb = {
11806             success: this.handleResponse,
11807             failure: this.handleFailure,
11808             scope: this,
11809             argument: {options: o},
11810             timeout : o.timeout || this.timeout
11811         };
11812  
11813         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11814             if(o.autoAbort){
11815                 this.abort();
11816             }
11817         }else if(this.autoAbort !== false){
11818             this.abort();
11819         }
11820
11821        
11822         this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
11823  
11824  
11825          
11826     }
11827     
11828 });
11829 /*
11830  * Based on:
11831  * Ext JS Library 1.1.1
11832  * Copyright(c) 2006-2007, Ext JS, LLC.
11833  *
11834  * Originally Released Under LGPL - original licence link has changed is not relivant.
11835  *
11836  * Fork - LGPL
11837  * <script type="text/javascript">
11838  */
11839  
11840 /**
11841  * Global Ajax request class.
11842  * 
11843  * @class Roo.Ajax
11844  * @extends Roo.data.Connection
11845  * @static
11846  * 
11847  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11848  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11849  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11850  * @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)
11851  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11852  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11853  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11854  */
11855 Roo.Ajax = new Roo.data.Connection({
11856     // fix up the docs
11857     /**
11858      * @scope Roo.Ajax
11859      * @type {Boolear} 
11860      */
11861     autoAbort : false,
11862
11863     /**
11864      * Serialize the passed form into a url encoded string
11865      * @scope Roo.Ajax
11866      * @param {String/HTMLElement} form
11867      * @return {String}
11868      */
11869     serializeForm : function(form){
11870         return Roo.lib.Ajax.serializeForm(form);
11871     }
11872 });/*
11873  * Based on:
11874  * Ext JS Library 1.1.1
11875  * Copyright(c) 2006-2007, Ext JS, LLC.
11876  *
11877  * Originally Released Under LGPL - original licence link has changed is not relivant.
11878  *
11879  * Fork - LGPL
11880  * <script type="text/javascript">
11881  */
11882
11883  
11884 /**
11885  * @class Roo.UpdateManager
11886  * @extends Roo.util.Observable
11887  * Provides AJAX-style update for Element object.<br><br>
11888  * Usage:<br>
11889  * <pre><code>
11890  * // Get it from a Roo.Element object
11891  * var el = Roo.get("foo");
11892  * var mgr = el.getUpdateManager();
11893  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11894  * ...
11895  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11896  * <br>
11897  * // or directly (returns the same UpdateManager instance)
11898  * var mgr = new Roo.UpdateManager("myElementId");
11899  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11900  * mgr.on("update", myFcnNeedsToKnow);
11901  * <br>
11902    // short handed call directly from the element object
11903    Roo.get("foo").load({
11904         url: "bar.php",
11905         scripts:true,
11906         params: "for=bar",
11907         text: "Loading Foo..."
11908    });
11909  * </code></pre>
11910  * @constructor
11911  * Create new UpdateManager directly.
11912  * @param {String/HTMLElement/Roo.Element} el The element to update
11913  * @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).
11914  */
11915 Roo.UpdateManager = function(el, forceNew){
11916     el = Roo.get(el);
11917     if(!forceNew && el.updateManager){
11918         return el.updateManager;
11919     }
11920     /**
11921      * The Element object
11922      * @type Roo.Element
11923      */
11924     this.el = el;
11925     /**
11926      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11927      * @type String
11928      */
11929     this.defaultUrl = null;
11930
11931     this.addEvents({
11932         /**
11933          * @event beforeupdate
11934          * Fired before an update is made, return false from your handler and the update is cancelled.
11935          * @param {Roo.Element} el
11936          * @param {String/Object/Function} url
11937          * @param {String/Object} params
11938          */
11939         "beforeupdate": true,
11940         /**
11941          * @event update
11942          * Fired after successful update is made.
11943          * @param {Roo.Element} el
11944          * @param {Object} oResponseObject The response Object
11945          */
11946         "update": true,
11947         /**
11948          * @event failure
11949          * Fired on update failure.
11950          * @param {Roo.Element} el
11951          * @param {Object} oResponseObject The response Object
11952          */
11953         "failure": true
11954     });
11955     var d = Roo.UpdateManager.defaults;
11956     /**
11957      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11958      * @type String
11959      */
11960     this.sslBlankUrl = d.sslBlankUrl;
11961     /**
11962      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11963      * @type Boolean
11964      */
11965     this.disableCaching = d.disableCaching;
11966     /**
11967      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11968      * @type String
11969      */
11970     this.indicatorText = d.indicatorText;
11971     /**
11972      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11973      * @type String
11974      */
11975     this.showLoadIndicator = d.showLoadIndicator;
11976     /**
11977      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11978      * @type Number
11979      */
11980     this.timeout = d.timeout;
11981
11982     /**
11983      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11984      * @type Boolean
11985      */
11986     this.loadScripts = d.loadScripts;
11987
11988     /**
11989      * Transaction object of current executing transaction
11990      */
11991     this.transaction = null;
11992
11993     /**
11994      * @private
11995      */
11996     this.autoRefreshProcId = null;
11997     /**
11998      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11999      * @type Function
12000      */
12001     this.refreshDelegate = this.refresh.createDelegate(this);
12002     /**
12003      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12004      * @type Function
12005      */
12006     this.updateDelegate = this.update.createDelegate(this);
12007     /**
12008      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12009      * @type Function
12010      */
12011     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12012     /**
12013      * @private
12014      */
12015     this.successDelegate = this.processSuccess.createDelegate(this);
12016     /**
12017      * @private
12018      */
12019     this.failureDelegate = this.processFailure.createDelegate(this);
12020
12021     if(!this.renderer){
12022      /**
12023       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12024       */
12025     this.renderer = new Roo.UpdateManager.BasicRenderer();
12026     }
12027     
12028     Roo.UpdateManager.superclass.constructor.call(this);
12029 };
12030
12031 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12032     /**
12033      * Get the Element this UpdateManager is bound to
12034      * @return {Roo.Element} The element
12035      */
12036     getEl : function(){
12037         return this.el;
12038     },
12039     /**
12040      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12041      * @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:
12042 <pre><code>
12043 um.update({<br/>
12044     url: "your-url.php",<br/>
12045     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12046     callback: yourFunction,<br/>
12047     scope: yourObject, //(optional scope)  <br/>
12048     discardUrl: false, <br/>
12049     nocache: false,<br/>
12050     text: "Loading...",<br/>
12051     timeout: 30,<br/>
12052     scripts: false<br/>
12053 });
12054 </code></pre>
12055      * The only required property is url. The optional properties nocache, text and scripts
12056      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12057      * @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}
12058      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12059      * @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.
12060      */
12061     update : function(url, params, callback, discardUrl){
12062         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12063             var method = this.method,
12064                 cfg;
12065             if(typeof url == "object"){ // must be config object
12066                 cfg = url;
12067                 url = cfg.url;
12068                 params = params || cfg.params;
12069                 callback = callback || cfg.callback;
12070                 discardUrl = discardUrl || cfg.discardUrl;
12071                 if(callback && cfg.scope){
12072                     callback = callback.createDelegate(cfg.scope);
12073                 }
12074                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12075                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12076                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12077                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12078                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12079             }
12080             this.showLoading();
12081             if(!discardUrl){
12082                 this.defaultUrl = url;
12083             }
12084             if(typeof url == "function"){
12085                 url = url.call(this);
12086             }
12087
12088             method = method || (params ? "POST" : "GET");
12089             if(method == "GET"){
12090                 url = this.prepareUrl(url);
12091             }
12092
12093             var o = Roo.apply(cfg ||{}, {
12094                 url : url,
12095                 params: params,
12096                 success: this.successDelegate,
12097                 failure: this.failureDelegate,
12098                 callback: undefined,
12099                 timeout: (this.timeout*1000),
12100                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12101             });
12102             Roo.log("updated manager called with timeout of " + o.timeout);
12103             this.transaction = Roo.Ajax.request(o);
12104         }
12105     },
12106
12107     /**
12108      * 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.
12109      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12110      * @param {String/HTMLElement} form The form Id or form element
12111      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12112      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12113      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12114      */
12115     formUpdate : function(form, url, reset, callback){
12116         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12117             if(typeof url == "function"){
12118                 url = url.call(this);
12119             }
12120             form = Roo.getDom(form);
12121             this.transaction = Roo.Ajax.request({
12122                 form: form,
12123                 url:url,
12124                 success: this.successDelegate,
12125                 failure: this.failureDelegate,
12126                 timeout: (this.timeout*1000),
12127                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12128             });
12129             this.showLoading.defer(1, this);
12130         }
12131     },
12132
12133     /**
12134      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12135      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12136      */
12137     refresh : function(callback){
12138         if(this.defaultUrl == null){
12139             return;
12140         }
12141         this.update(this.defaultUrl, null, callback, true);
12142     },
12143
12144     /**
12145      * Set this element to auto refresh.
12146      * @param {Number} interval How often to update (in seconds).
12147      * @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)
12148      * @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}
12149      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12150      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12151      */
12152     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12153         if(refreshNow){
12154             this.update(url || this.defaultUrl, params, callback, true);
12155         }
12156         if(this.autoRefreshProcId){
12157             clearInterval(this.autoRefreshProcId);
12158         }
12159         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12160     },
12161
12162     /**
12163      * Stop auto refresh on this element.
12164      */
12165      stopAutoRefresh : function(){
12166         if(this.autoRefreshProcId){
12167             clearInterval(this.autoRefreshProcId);
12168             delete this.autoRefreshProcId;
12169         }
12170     },
12171
12172     isAutoRefreshing : function(){
12173        return this.autoRefreshProcId ? true : false;
12174     },
12175     /**
12176      * Called to update the element to "Loading" state. Override to perform custom action.
12177      */
12178     showLoading : function(){
12179         if(this.showLoadIndicator){
12180             this.el.update(this.indicatorText);
12181         }
12182     },
12183
12184     /**
12185      * Adds unique parameter to query string if disableCaching = true
12186      * @private
12187      */
12188     prepareUrl : function(url){
12189         if(this.disableCaching){
12190             var append = "_dc=" + (new Date().getTime());
12191             if(url.indexOf("?") !== -1){
12192                 url += "&" + append;
12193             }else{
12194                 url += "?" + append;
12195             }
12196         }
12197         return url;
12198     },
12199
12200     /**
12201      * @private
12202      */
12203     processSuccess : function(response){
12204         this.transaction = null;
12205         if(response.argument.form && response.argument.reset){
12206             try{ // put in try/catch since some older FF releases had problems with this
12207                 response.argument.form.reset();
12208             }catch(e){}
12209         }
12210         if(this.loadScripts){
12211             this.renderer.render(this.el, response, this,
12212                 this.updateComplete.createDelegate(this, [response]));
12213         }else{
12214             this.renderer.render(this.el, response, this);
12215             this.updateComplete(response);
12216         }
12217     },
12218
12219     updateComplete : function(response){
12220         this.fireEvent("update", this.el, response);
12221         if(typeof response.argument.callback == "function"){
12222             response.argument.callback(this.el, true, response);
12223         }
12224     },
12225
12226     /**
12227      * @private
12228      */
12229     processFailure : function(response){
12230         this.transaction = null;
12231         this.fireEvent("failure", this.el, response);
12232         if(typeof response.argument.callback == "function"){
12233             response.argument.callback(this.el, false, response);
12234         }
12235     },
12236
12237     /**
12238      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12239      * @param {Object} renderer The object implementing the render() method
12240      */
12241     setRenderer : function(renderer){
12242         this.renderer = renderer;
12243     },
12244
12245     getRenderer : function(){
12246        return this.renderer;
12247     },
12248
12249     /**
12250      * Set the defaultUrl used for updates
12251      * @param {String/Function} defaultUrl The url or a function to call to get the url
12252      */
12253     setDefaultUrl : function(defaultUrl){
12254         this.defaultUrl = defaultUrl;
12255     },
12256
12257     /**
12258      * Aborts the executing transaction
12259      */
12260     abort : function(){
12261         if(this.transaction){
12262             Roo.Ajax.abort(this.transaction);
12263         }
12264     },
12265
12266     /**
12267      * Returns true if an update is in progress
12268      * @return {Boolean}
12269      */
12270     isUpdating : function(){
12271         if(this.transaction){
12272             return Roo.Ajax.isLoading(this.transaction);
12273         }
12274         return false;
12275     }
12276 });
12277
12278 /**
12279  * @class Roo.UpdateManager.defaults
12280  * @static (not really - but it helps the doc tool)
12281  * The defaults collection enables customizing the default properties of UpdateManager
12282  */
12283    Roo.UpdateManager.defaults = {
12284        /**
12285          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12286          * @type Number
12287          */
12288          timeout : 30,
12289
12290          /**
12291          * True to process scripts by default (Defaults to false).
12292          * @type Boolean
12293          */
12294         loadScripts : false,
12295
12296         /**
12297         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12298         * @type String
12299         */
12300         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12301         /**
12302          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12303          * @type Boolean
12304          */
12305         disableCaching : false,
12306         /**
12307          * Whether to show indicatorText when loading (Defaults to true).
12308          * @type Boolean
12309          */
12310         showLoadIndicator : true,
12311         /**
12312          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12313          * @type String
12314          */
12315         indicatorText : '<div class="loading-indicator">Loading...</div>'
12316    };
12317
12318 /**
12319  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12320  *Usage:
12321  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12322  * @param {String/HTMLElement/Roo.Element} el The element to update
12323  * @param {String} url The url
12324  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12325  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12326  * @static
12327  * @deprecated
12328  * @member Roo.UpdateManager
12329  */
12330 Roo.UpdateManager.updateElement = function(el, url, params, options){
12331     var um = Roo.get(el, true).getUpdateManager();
12332     Roo.apply(um, options);
12333     um.update(url, params, options ? options.callback : null);
12334 };
12335 // alias for backwards compat
12336 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12337 /**
12338  * @class Roo.UpdateManager.BasicRenderer
12339  * Default Content renderer. Updates the elements innerHTML with the responseText.
12340  */
12341 Roo.UpdateManager.BasicRenderer = function(){};
12342
12343 Roo.UpdateManager.BasicRenderer.prototype = {
12344     /**
12345      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12346      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12347      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12348      * @param {Roo.Element} el The element being rendered
12349      * @param {Object} response The YUI Connect response object
12350      * @param {UpdateManager} updateManager The calling update manager
12351      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12352      */
12353      render : function(el, response, updateManager, callback){
12354         el.update(response.responseText, updateManager.loadScripts, callback);
12355     }
12356 };
12357 /*
12358  * Based on:
12359  * Roo JS
12360  * (c)) Alan Knowles
12361  * Licence : LGPL
12362  */
12363
12364
12365 /**
12366  * @class Roo.DomTemplate
12367  * @extends Roo.Template
12368  * An effort at a dom based template engine..
12369  *
12370  * Similar to XTemplate, except it uses dom parsing to create the template..
12371  *
12372  * Supported features:
12373  *
12374  *  Tags:
12375
12376 <pre><code>
12377       {a_variable} - output encoded.
12378       {a_variable.format:("Y-m-d")} - call a method on the variable
12379       {a_variable:raw} - unencoded output
12380       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12381       {a_variable:this.method_on_template(...)} - call a method on the template object.
12382  
12383 </code></pre>
12384  *  The tpl tag:
12385 <pre><code>
12386         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12387         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12388         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12389         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12390   
12391 </code></pre>
12392  *      
12393  */
12394 Roo.DomTemplate = function()
12395 {
12396      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12397      if (this.html) {
12398         this.compile();
12399      }
12400 };
12401
12402
12403 Roo.extend(Roo.DomTemplate, Roo.Template, {
12404     /**
12405      * id counter for sub templates.
12406      */
12407     id : 0,
12408     /**
12409      * flag to indicate if dom parser is inside a pre,
12410      * it will strip whitespace if not.
12411      */
12412     inPre : false,
12413     
12414     /**
12415      * The various sub templates
12416      */
12417     tpls : false,
12418     
12419     
12420     
12421     /**
12422      *
12423      * basic tag replacing syntax
12424      * WORD:WORD()
12425      *
12426      * // you can fake an object call by doing this
12427      *  x.t:(test,tesT) 
12428      * 
12429      */
12430     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12431     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12432     
12433     iterChild : function (node, method) {
12434         
12435         var oldPre = this.inPre;
12436         if (node.tagName == 'PRE') {
12437             this.inPre = true;
12438         }
12439         for( var i = 0; i < node.childNodes.length; i++) {
12440             method.call(this, node.childNodes[i]);
12441         }
12442         this.inPre = oldPre;
12443     },
12444     
12445     
12446     
12447     /**
12448      * compile the template
12449      *
12450      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12451      *
12452      */
12453     compile: function()
12454     {
12455         var s = this.html;
12456         
12457         // covert the html into DOM...
12458         var doc = false;
12459         var div =false;
12460         try {
12461             doc = document.implementation.createHTMLDocument("");
12462             doc.documentElement.innerHTML =   this.html  ;
12463             div = doc.documentElement;
12464         } catch (e) {
12465             // old IE... - nasty -- it causes all sorts of issues.. with
12466             // images getting pulled from server..
12467             div = document.createElement('div');
12468             div.innerHTML = this.html;
12469         }
12470         //doc.documentElement.innerHTML = htmlBody
12471          
12472         
12473         
12474         this.tpls = [];
12475         var _t = this;
12476         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12477         
12478         var tpls = this.tpls;
12479         
12480         // create a top level template from the snippet..
12481         
12482         //Roo.log(div.innerHTML);
12483         
12484         var tpl = {
12485             uid : 'master',
12486             id : this.id++,
12487             attr : false,
12488             value : false,
12489             body : div.innerHTML,
12490             
12491             forCall : false,
12492             execCall : false,
12493             dom : div,
12494             isTop : true
12495             
12496         };
12497         tpls.unshift(tpl);
12498         
12499         
12500         // compile them...
12501         this.tpls = [];
12502         Roo.each(tpls, function(tp){
12503             this.compileTpl(tp);
12504             this.tpls[tp.id] = tp;
12505         }, this);
12506         
12507         this.master = tpls[0];
12508         return this;
12509         
12510         
12511     },
12512     
12513     compileNode : function(node, istop) {
12514         // test for
12515         //Roo.log(node);
12516         
12517         
12518         // skip anything not a tag..
12519         if (node.nodeType != 1) {
12520             if (node.nodeType == 3 && !this.inPre) {
12521                 // reduce white space..
12522                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12523                 
12524             }
12525             return;
12526         }
12527         
12528         var tpl = {
12529             uid : false,
12530             id : false,
12531             attr : false,
12532             value : false,
12533             body : '',
12534             
12535             forCall : false,
12536             execCall : false,
12537             dom : false,
12538             isTop : istop
12539             
12540             
12541         };
12542         
12543         
12544         switch(true) {
12545             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12546             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12547             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12548             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12549             // no default..
12550         }
12551         
12552         
12553         if (!tpl.attr) {
12554             // just itterate children..
12555             this.iterChild(node,this.compileNode);
12556             return;
12557         }
12558         tpl.uid = this.id++;
12559         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12560         node.removeAttribute('roo-'+ tpl.attr);
12561         if (tpl.attr != 'name') {
12562             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12563             node.parentNode.replaceChild(placeholder,  node);
12564         } else {
12565             
12566             var placeholder =  document.createElement('span');
12567             placeholder.className = 'roo-tpl-' + tpl.value;
12568             node.parentNode.replaceChild(placeholder,  node);
12569         }
12570         
12571         // parent now sees '{domtplXXXX}
12572         this.iterChild(node,this.compileNode);
12573         
12574         // we should now have node body...
12575         var div = document.createElement('div');
12576         div.appendChild(node);
12577         tpl.dom = node;
12578         // this has the unfortunate side effect of converting tagged attributes
12579         // eg. href="{...}" into %7C...%7D
12580         // this has been fixed by searching for those combo's although it's a bit hacky..
12581         
12582         
12583         tpl.body = div.innerHTML;
12584         
12585         
12586          
12587         tpl.id = tpl.uid;
12588         switch(tpl.attr) {
12589             case 'for' :
12590                 switch (tpl.value) {
12591                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12592                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12593                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12594                 }
12595                 break;
12596             
12597             case 'exec':
12598                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12599                 break;
12600             
12601             case 'if':     
12602                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12603                 break;
12604             
12605             case 'name':
12606                 tpl.id  = tpl.value; // replace non characters???
12607                 break;
12608             
12609         }
12610         
12611         
12612         this.tpls.push(tpl);
12613         
12614         
12615         
12616     },
12617     
12618     
12619     
12620     
12621     /**
12622      * Compile a segment of the template into a 'sub-template'
12623      *
12624      * 
12625      * 
12626      *
12627      */
12628     compileTpl : function(tpl)
12629     {
12630         var fm = Roo.util.Format;
12631         var useF = this.disableFormats !== true;
12632         
12633         var sep = Roo.isGecko ? "+\n" : ",\n";
12634         
12635         var undef = function(str) {
12636             Roo.debug && Roo.log("Property not found :"  + str);
12637             return '';
12638         };
12639           
12640         //Roo.log(tpl.body);
12641         
12642         
12643         
12644         var fn = function(m, lbrace, name, format, args)
12645         {
12646             //Roo.log("ARGS");
12647             //Roo.log(arguments);
12648             args = args ? args.replace(/\\'/g,"'") : args;
12649             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12650             if (typeof(format) == 'undefined') {
12651                 format =  'htmlEncode'; 
12652             }
12653             if (format == 'raw' ) {
12654                 format = false;
12655             }
12656             
12657             if(name.substr(0, 6) == 'domtpl'){
12658                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12659             }
12660             
12661             // build an array of options to determine if value is undefined..
12662             
12663             // basically get 'xxxx.yyyy' then do
12664             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12665             //    (function () { Roo.log("Property not found"); return ''; })() :
12666             //    ......
12667             
12668             var udef_ar = [];
12669             var lookfor = '';
12670             Roo.each(name.split('.'), function(st) {
12671                 lookfor += (lookfor.length ? '.': '') + st;
12672                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12673             });
12674             
12675             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12676             
12677             
12678             if(format && useF){
12679                 
12680                 args = args ? ',' + args : "";
12681                  
12682                 if(format.substr(0, 5) != "this."){
12683                     format = "fm." + format + '(';
12684                 }else{
12685                     format = 'this.call("'+ format.substr(5) + '", ';
12686                     args = ", values";
12687                 }
12688                 
12689                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12690             }
12691              
12692             if (args && args.length) {
12693                 // called with xxyx.yuu:(test,test)
12694                 // change to ()
12695                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12696             }
12697             // raw.. - :raw modifier..
12698             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12699             
12700         };
12701         var body;
12702         // branched to use + in gecko and [].join() in others
12703         if(Roo.isGecko){
12704             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12705                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12706                     "';};};";
12707         }else{
12708             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12709             body.push(tpl.body.replace(/(\r\n|\n)/g,
12710                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12711             body.push("'].join('');};};");
12712             body = body.join('');
12713         }
12714         
12715         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12716        
12717         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12718         eval(body);
12719         
12720         return this;
12721     },
12722      
12723     /**
12724      * same as applyTemplate, except it's done to one of the subTemplates
12725      * when using named templates, you can do:
12726      *
12727      * var str = pl.applySubTemplate('your-name', values);
12728      *
12729      * 
12730      * @param {Number} id of the template
12731      * @param {Object} values to apply to template
12732      * @param {Object} parent (normaly the instance of this object)
12733      */
12734     applySubTemplate : function(id, values, parent)
12735     {
12736         
12737         
12738         var t = this.tpls[id];
12739         
12740         
12741         try { 
12742             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12743                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12744                 return '';
12745             }
12746         } catch(e) {
12747             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12748             Roo.log(values);
12749           
12750             return '';
12751         }
12752         try { 
12753             
12754             if(t.execCall && t.execCall.call(this, values, parent)){
12755                 return '';
12756             }
12757         } catch(e) {
12758             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12759             Roo.log(values);
12760             return '';
12761         }
12762         
12763         try {
12764             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12765             parent = t.target ? values : parent;
12766             if(t.forCall && vs instanceof Array){
12767                 var buf = [];
12768                 for(var i = 0, len = vs.length; i < len; i++){
12769                     try {
12770                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12771                     } catch (e) {
12772                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12773                         Roo.log(e.body);
12774                         //Roo.log(t.compiled);
12775                         Roo.log(vs[i]);
12776                     }   
12777                 }
12778                 return buf.join('');
12779             }
12780         } catch (e) {
12781             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12782             Roo.log(values);
12783             return '';
12784         }
12785         try {
12786             return t.compiled.call(this, vs, parent);
12787         } catch (e) {
12788             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12789             Roo.log(e.body);
12790             //Roo.log(t.compiled);
12791             Roo.log(values);
12792             return '';
12793         }
12794     },
12795
12796    
12797
12798     applyTemplate : function(values){
12799         return this.master.compiled.call(this, values, {});
12800         //var s = this.subs;
12801     },
12802
12803     apply : function(){
12804         return this.applyTemplate.apply(this, arguments);
12805     }
12806
12807  });
12808
12809 Roo.DomTemplate.from = function(el){
12810     el = Roo.getDom(el);
12811     return new Roo.Domtemplate(el.value || el.innerHTML);
12812 };/*
12813  * Based on:
12814  * Ext JS Library 1.1.1
12815  * Copyright(c) 2006-2007, Ext JS, LLC.
12816  *
12817  * Originally Released Under LGPL - original licence link has changed is not relivant.
12818  *
12819  * Fork - LGPL
12820  * <script type="text/javascript">
12821  */
12822
12823 /**
12824  * @class Roo.util.DelayedTask
12825  * Provides a convenient method of performing setTimeout where a new
12826  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12827  * You can use this class to buffer
12828  * the keypress events for a certain number of milliseconds, and perform only if they stop
12829  * for that amount of time.
12830  * @constructor The parameters to this constructor serve as defaults and are not required.
12831  * @param {Function} fn (optional) The default function to timeout
12832  * @param {Object} scope (optional) The default scope of that timeout
12833  * @param {Array} args (optional) The default Array of arguments
12834  */
12835 Roo.util.DelayedTask = function(fn, scope, args){
12836     var id = null, d, t;
12837
12838     var call = function(){
12839         var now = new Date().getTime();
12840         if(now - t >= d){
12841             clearInterval(id);
12842             id = null;
12843             fn.apply(scope, args || []);
12844         }
12845     };
12846     /**
12847      * Cancels any pending timeout and queues a new one
12848      * @param {Number} delay The milliseconds to delay
12849      * @param {Function} newFn (optional) Overrides function passed to constructor
12850      * @param {Object} newScope (optional) Overrides scope passed to constructor
12851      * @param {Array} newArgs (optional) Overrides args passed to constructor
12852      */
12853     this.delay = function(delay, newFn, newScope, newArgs){
12854         if(id && delay != d){
12855             this.cancel();
12856         }
12857         d = delay;
12858         t = new Date().getTime();
12859         fn = newFn || fn;
12860         scope = newScope || scope;
12861         args = newArgs || args;
12862         if(!id){
12863             id = setInterval(call, d);
12864         }
12865     };
12866
12867     /**
12868      * Cancel the last queued timeout
12869      */
12870     this.cancel = function(){
12871         if(id){
12872             clearInterval(id);
12873             id = null;
12874         }
12875     };
12876 };/*
12877  * Based on:
12878  * Ext JS Library 1.1.1
12879  * Copyright(c) 2006-2007, Ext JS, LLC.
12880  *
12881  * Originally Released Under LGPL - original licence link has changed is not relivant.
12882  *
12883  * Fork - LGPL
12884  * <script type="text/javascript">
12885  */
12886  
12887  
12888 Roo.util.TaskRunner = function(interval){
12889     interval = interval || 10;
12890     var tasks = [], removeQueue = [];
12891     var id = 0;
12892     var running = false;
12893
12894     var stopThread = function(){
12895         running = false;
12896         clearInterval(id);
12897         id = 0;
12898     };
12899
12900     var startThread = function(){
12901         if(!running){
12902             running = true;
12903             id = setInterval(runTasks, interval);
12904         }
12905     };
12906
12907     var removeTask = function(task){
12908         removeQueue.push(task);
12909         if(task.onStop){
12910             task.onStop();
12911         }
12912     };
12913
12914     var runTasks = function(){
12915         if(removeQueue.length > 0){
12916             for(var i = 0, len = removeQueue.length; i < len; i++){
12917                 tasks.remove(removeQueue[i]);
12918             }
12919             removeQueue = [];
12920             if(tasks.length < 1){
12921                 stopThread();
12922                 return;
12923             }
12924         }
12925         var now = new Date().getTime();
12926         for(var i = 0, len = tasks.length; i < len; ++i){
12927             var t = tasks[i];
12928             var itime = now - t.taskRunTime;
12929             if(t.interval <= itime){
12930                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12931                 t.taskRunTime = now;
12932                 if(rt === false || t.taskRunCount === t.repeat){
12933                     removeTask(t);
12934                     return;
12935                 }
12936             }
12937             if(t.duration && t.duration <= (now - t.taskStartTime)){
12938                 removeTask(t);
12939             }
12940         }
12941     };
12942
12943     /**
12944      * Queues a new task.
12945      * @param {Object} task
12946      */
12947     this.start = function(task){
12948         tasks.push(task);
12949         task.taskStartTime = new Date().getTime();
12950         task.taskRunTime = 0;
12951         task.taskRunCount = 0;
12952         startThread();
12953         return task;
12954     };
12955
12956     this.stop = function(task){
12957         removeTask(task);
12958         return task;
12959     };
12960
12961     this.stopAll = function(){
12962         stopThread();
12963         for(var i = 0, len = tasks.length; i < len; i++){
12964             if(tasks[i].onStop){
12965                 tasks[i].onStop();
12966             }
12967         }
12968         tasks = [];
12969         removeQueue = [];
12970     };
12971 };
12972
12973 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12974  * Based on:
12975  * Ext JS Library 1.1.1
12976  * Copyright(c) 2006-2007, Ext JS, LLC.
12977  *
12978  * Originally Released Under LGPL - original licence link has changed is not relivant.
12979  *
12980  * Fork - LGPL
12981  * <script type="text/javascript">
12982  */
12983
12984  
12985 /**
12986  * @class Roo.util.MixedCollection
12987  * @extends Roo.util.Observable
12988  * A Collection class that maintains both numeric indexes and keys and exposes events.
12989  * @constructor
12990  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12991  * collection (defaults to false)
12992  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12993  * and return the key value for that item.  This is used when available to look up the key on items that
12994  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12995  * equivalent to providing an implementation for the {@link #getKey} method.
12996  */
12997 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12998     this.items = [];
12999     this.map = {};
13000     this.keys = [];
13001     this.length = 0;
13002     this.addEvents({
13003         /**
13004          * @event clear
13005          * Fires when the collection is cleared.
13006          */
13007         "clear" : true,
13008         /**
13009          * @event add
13010          * Fires when an item is added to the collection.
13011          * @param {Number} index The index at which the item was added.
13012          * @param {Object} o The item added.
13013          * @param {String} key The key associated with the added item.
13014          */
13015         "add" : true,
13016         /**
13017          * @event replace
13018          * Fires when an item is replaced in the collection.
13019          * @param {String} key he key associated with the new added.
13020          * @param {Object} old The item being replaced.
13021          * @param {Object} new The new item.
13022          */
13023         "replace" : true,
13024         /**
13025          * @event remove
13026          * Fires when an item is removed from the collection.
13027          * @param {Object} o The item being removed.
13028          * @param {String} key (optional) The key associated with the removed item.
13029          */
13030         "remove" : true,
13031         "sort" : true
13032     });
13033     this.allowFunctions = allowFunctions === true;
13034     if(keyFn){
13035         this.getKey = keyFn;
13036     }
13037     Roo.util.MixedCollection.superclass.constructor.call(this);
13038 };
13039
13040 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13041     allowFunctions : false,
13042     
13043 /**
13044  * Adds an item to the collection.
13045  * @param {String} key The key to associate with the item
13046  * @param {Object} o The item to add.
13047  * @return {Object} The item added.
13048  */
13049     add : function(key, o){
13050         if(arguments.length == 1){
13051             o = arguments[0];
13052             key = this.getKey(o);
13053         }
13054         if(typeof key == "undefined" || key === null){
13055             this.length++;
13056             this.items.push(o);
13057             this.keys.push(null);
13058         }else{
13059             var old = this.map[key];
13060             if(old){
13061                 return this.replace(key, o);
13062             }
13063             this.length++;
13064             this.items.push(o);
13065             this.map[key] = o;
13066             this.keys.push(key);
13067         }
13068         this.fireEvent("add", this.length-1, o, key);
13069         return o;
13070     },
13071        
13072 /**
13073   * MixedCollection has a generic way to fetch keys if you implement getKey.
13074 <pre><code>
13075 // normal way
13076 var mc = new Roo.util.MixedCollection();
13077 mc.add(someEl.dom.id, someEl);
13078 mc.add(otherEl.dom.id, otherEl);
13079 //and so on
13080
13081 // using getKey
13082 var mc = new Roo.util.MixedCollection();
13083 mc.getKey = function(el){
13084    return el.dom.id;
13085 };
13086 mc.add(someEl);
13087 mc.add(otherEl);
13088
13089 // or via the constructor
13090 var mc = new Roo.util.MixedCollection(false, function(el){
13091    return el.dom.id;
13092 });
13093 mc.add(someEl);
13094 mc.add(otherEl);
13095 </code></pre>
13096  * @param o {Object} The item for which to find the key.
13097  * @return {Object} The key for the passed item.
13098  */
13099     getKey : function(o){
13100          return o.id; 
13101     },
13102    
13103 /**
13104  * Replaces an item in the collection.
13105  * @param {String} key The key associated with the item to replace, or the item to replace.
13106  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13107  * @return {Object}  The new item.
13108  */
13109     replace : function(key, o){
13110         if(arguments.length == 1){
13111             o = arguments[0];
13112             key = this.getKey(o);
13113         }
13114         var old = this.item(key);
13115         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13116              return this.add(key, o);
13117         }
13118         var index = this.indexOfKey(key);
13119         this.items[index] = o;
13120         this.map[key] = o;
13121         this.fireEvent("replace", key, old, o);
13122         return o;
13123     },
13124    
13125 /**
13126  * Adds all elements of an Array or an Object to the collection.
13127  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13128  * an Array of values, each of which are added to the collection.
13129  */
13130     addAll : function(objs){
13131         if(arguments.length > 1 || objs instanceof Array){
13132             var args = arguments.length > 1 ? arguments : objs;
13133             for(var i = 0, len = args.length; i < len; i++){
13134                 this.add(args[i]);
13135             }
13136         }else{
13137             for(var key in objs){
13138                 if(this.allowFunctions || typeof objs[key] != "function"){
13139                     this.add(key, objs[key]);
13140                 }
13141             }
13142         }
13143     },
13144    
13145 /**
13146  * Executes the specified function once for every item in the collection, passing each
13147  * item as the first and only parameter. returning false from the function will stop the iteration.
13148  * @param {Function} fn The function to execute for each item.
13149  * @param {Object} scope (optional) The scope in which to execute the function.
13150  */
13151     each : function(fn, scope){
13152         var items = [].concat(this.items); // each safe for removal
13153         for(var i = 0, len = items.length; i < len; i++){
13154             if(fn.call(scope || items[i], items[i], i, len) === false){
13155                 break;
13156             }
13157         }
13158     },
13159    
13160 /**
13161  * Executes the specified function once for every key in the collection, passing each
13162  * key, and its associated item as the first two parameters.
13163  * @param {Function} fn The function to execute for each item.
13164  * @param {Object} scope (optional) The scope in which to execute the function.
13165  */
13166     eachKey : function(fn, scope){
13167         for(var i = 0, len = this.keys.length; i < len; i++){
13168             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13169         }
13170     },
13171    
13172 /**
13173  * Returns the first item in the collection which elicits a true return value from the
13174  * passed selection function.
13175  * @param {Function} fn The selection function to execute for each item.
13176  * @param {Object} scope (optional) The scope in which to execute the function.
13177  * @return {Object} The first item in the collection which returned true from the selection function.
13178  */
13179     find : function(fn, scope){
13180         for(var i = 0, len = this.items.length; i < len; i++){
13181             if(fn.call(scope || window, this.items[i], this.keys[i])){
13182                 return this.items[i];
13183             }
13184         }
13185         return null;
13186     },
13187    
13188 /**
13189  * Inserts an item at the specified index in the collection.
13190  * @param {Number} index The index to insert the item at.
13191  * @param {String} key The key to associate with the new item, or the item itself.
13192  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13193  * @return {Object} The item inserted.
13194  */
13195     insert : function(index, key, o){
13196         if(arguments.length == 2){
13197             o = arguments[1];
13198             key = this.getKey(o);
13199         }
13200         if(index >= this.length){
13201             return this.add(key, o);
13202         }
13203         this.length++;
13204         this.items.splice(index, 0, o);
13205         if(typeof key != "undefined" && key != null){
13206             this.map[key] = o;
13207         }
13208         this.keys.splice(index, 0, key);
13209         this.fireEvent("add", index, o, key);
13210         return o;
13211     },
13212    
13213 /**
13214  * Removed an item from the collection.
13215  * @param {Object} o The item to remove.
13216  * @return {Object} The item removed.
13217  */
13218     remove : function(o){
13219         return this.removeAt(this.indexOf(o));
13220     },
13221    
13222 /**
13223  * Remove an item from a specified index in the collection.
13224  * @param {Number} index The index within the collection of the item to remove.
13225  */
13226     removeAt : function(index){
13227         if(index < this.length && index >= 0){
13228             this.length--;
13229             var o = this.items[index];
13230             this.items.splice(index, 1);
13231             var key = this.keys[index];
13232             if(typeof key != "undefined"){
13233                 delete this.map[key];
13234             }
13235             this.keys.splice(index, 1);
13236             this.fireEvent("remove", o, key);
13237         }
13238     },
13239    
13240 /**
13241  * Removed an item associated with the passed key fom the collection.
13242  * @param {String} key The key of the item to remove.
13243  */
13244     removeKey : function(key){
13245         return this.removeAt(this.indexOfKey(key));
13246     },
13247    
13248 /**
13249  * Returns the number of items in the collection.
13250  * @return {Number} the number of items in the collection.
13251  */
13252     getCount : function(){
13253         return this.length; 
13254     },
13255    
13256 /**
13257  * Returns index within the collection of the passed Object.
13258  * @param {Object} o The item to find the index of.
13259  * @return {Number} index of the item.
13260  */
13261     indexOf : function(o){
13262         if(!this.items.indexOf){
13263             for(var i = 0, len = this.items.length; i < len; i++){
13264                 if(this.items[i] == o) {
13265                     return i;
13266                 }
13267             }
13268             return -1;
13269         }else{
13270             return this.items.indexOf(o);
13271         }
13272     },
13273    
13274 /**
13275  * Returns index within the collection of the passed key.
13276  * @param {String} key The key to find the index of.
13277  * @return {Number} index of the key.
13278  */
13279     indexOfKey : function(key){
13280         if(!this.keys.indexOf){
13281             for(var i = 0, len = this.keys.length; i < len; i++){
13282                 if(this.keys[i] == key) {
13283                     return i;
13284                 }
13285             }
13286             return -1;
13287         }else{
13288             return this.keys.indexOf(key);
13289         }
13290     },
13291    
13292 /**
13293  * Returns the item associated with the passed key OR index. Key has priority over index.
13294  * @param {String/Number} key The key or index of the item.
13295  * @return {Object} The item associated with the passed key.
13296  */
13297     item : function(key){
13298         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13299         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13300     },
13301     
13302 /**
13303  * Returns the item at the specified index.
13304  * @param {Number} index The index of the item.
13305  * @return {Object}
13306  */
13307     itemAt : function(index){
13308         return this.items[index];
13309     },
13310     
13311 /**
13312  * Returns the item associated with the passed key.
13313  * @param {String/Number} key The key of the item.
13314  * @return {Object} The item associated with the passed key.
13315  */
13316     key : function(key){
13317         return this.map[key];
13318     },
13319    
13320 /**
13321  * Returns true if the collection contains the passed Object as an item.
13322  * @param {Object} o  The Object to look for in the collection.
13323  * @return {Boolean} True if the collection contains the Object as an item.
13324  */
13325     contains : function(o){
13326         return this.indexOf(o) != -1;
13327     },
13328    
13329 /**
13330  * Returns true if the collection contains the passed Object as a key.
13331  * @param {String} key The key to look for in the collection.
13332  * @return {Boolean} True if the collection contains the Object as a key.
13333  */
13334     containsKey : function(key){
13335         return typeof this.map[key] != "undefined";
13336     },
13337    
13338 /**
13339  * Removes all items from the collection.
13340  */
13341     clear : function(){
13342         this.length = 0;
13343         this.items = [];
13344         this.keys = [];
13345         this.map = {};
13346         this.fireEvent("clear");
13347     },
13348    
13349 /**
13350  * Returns the first item in the collection.
13351  * @return {Object} the first item in the collection..
13352  */
13353     first : function(){
13354         return this.items[0]; 
13355     },
13356    
13357 /**
13358  * Returns the last item in the collection.
13359  * @return {Object} the last item in the collection..
13360  */
13361     last : function(){
13362         return this.items[this.length-1];   
13363     },
13364     
13365     _sort : function(property, dir, fn){
13366         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13367         fn = fn || function(a, b){
13368             return a-b;
13369         };
13370         var c = [], k = this.keys, items = this.items;
13371         for(var i = 0, len = items.length; i < len; i++){
13372             c[c.length] = {key: k[i], value: items[i], index: i};
13373         }
13374         c.sort(function(a, b){
13375             var v = fn(a[property], b[property]) * dsc;
13376             if(v == 0){
13377                 v = (a.index < b.index ? -1 : 1);
13378             }
13379             return v;
13380         });
13381         for(var i = 0, len = c.length; i < len; i++){
13382             items[i] = c[i].value;
13383             k[i] = c[i].key;
13384         }
13385         this.fireEvent("sort", this);
13386     },
13387     
13388     /**
13389      * Sorts this collection with the passed comparison function
13390      * @param {String} direction (optional) "ASC" or "DESC"
13391      * @param {Function} fn (optional) comparison function
13392      */
13393     sort : function(dir, fn){
13394         this._sort("value", dir, fn);
13395     },
13396     
13397     /**
13398      * Sorts this collection by keys
13399      * @param {String} direction (optional) "ASC" or "DESC"
13400      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13401      */
13402     keySort : function(dir, fn){
13403         this._sort("key", dir, fn || function(a, b){
13404             return String(a).toUpperCase()-String(b).toUpperCase();
13405         });
13406     },
13407     
13408     /**
13409      * Returns a range of items in this collection
13410      * @param {Number} startIndex (optional) defaults to 0
13411      * @param {Number} endIndex (optional) default to the last item
13412      * @return {Array} An array of items
13413      */
13414     getRange : function(start, end){
13415         var items = this.items;
13416         if(items.length < 1){
13417             return [];
13418         }
13419         start = start || 0;
13420         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13421         var r = [];
13422         if(start <= end){
13423             for(var i = start; i <= end; i++) {
13424                     r[r.length] = items[i];
13425             }
13426         }else{
13427             for(var i = start; i >= end; i--) {
13428                     r[r.length] = items[i];
13429             }
13430         }
13431         return r;
13432     },
13433         
13434     /**
13435      * Filter the <i>objects</i> in this collection by a specific property. 
13436      * Returns a new collection that has been filtered.
13437      * @param {String} property A property on your objects
13438      * @param {String/RegExp} value Either string that the property values 
13439      * should start with or a RegExp to test against the property
13440      * @return {MixedCollection} The new filtered collection
13441      */
13442     filter : function(property, value){
13443         if(!value.exec){ // not a regex
13444             value = String(value);
13445             if(value.length == 0){
13446                 return this.clone();
13447             }
13448             value = new RegExp("^" + Roo.escapeRe(value), "i");
13449         }
13450         return this.filterBy(function(o){
13451             return o && value.test(o[property]);
13452         });
13453         },
13454     
13455     /**
13456      * Filter by a function. * Returns a new collection that has been filtered.
13457      * The passed function will be called with each 
13458      * object in the collection. If the function returns true, the value is included 
13459      * otherwise it is filtered.
13460      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13461      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13462      * @return {MixedCollection} The new filtered collection
13463      */
13464     filterBy : function(fn, scope){
13465         var r = new Roo.util.MixedCollection();
13466         r.getKey = this.getKey;
13467         var k = this.keys, it = this.items;
13468         for(var i = 0, len = it.length; i < len; i++){
13469             if(fn.call(scope||this, it[i], k[i])){
13470                                 r.add(k[i], it[i]);
13471                         }
13472         }
13473         return r;
13474     },
13475     
13476     /**
13477      * Creates a duplicate of this collection
13478      * @return {MixedCollection}
13479      */
13480     clone : function(){
13481         var r = new Roo.util.MixedCollection();
13482         var k = this.keys, it = this.items;
13483         for(var i = 0, len = it.length; i < len; i++){
13484             r.add(k[i], it[i]);
13485         }
13486         r.getKey = this.getKey;
13487         return r;
13488     }
13489 });
13490 /**
13491  * Returns the item associated with the passed key or index.
13492  * @method
13493  * @param {String/Number} key The key or index of the item.
13494  * @return {Object} The item associated with the passed key.
13495  */
13496 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13497  * Based on:
13498  * Ext JS Library 1.1.1
13499  * Copyright(c) 2006-2007, Ext JS, LLC.
13500  *
13501  * Originally Released Under LGPL - original licence link has changed is not relivant.
13502  *
13503  * Fork - LGPL
13504  * <script type="text/javascript">
13505  */
13506 /**
13507  * @class Roo.util.JSON
13508  * Modified version of Douglas Crockford"s json.js that doesn"t
13509  * mess with the Object prototype 
13510  * http://www.json.org/js.html
13511  * @singleton
13512  */
13513 Roo.util.JSON = new (function(){
13514     var useHasOwn = {}.hasOwnProperty ? true : false;
13515     
13516     // crashes Safari in some instances
13517     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13518     
13519     var pad = function(n) {
13520         return n < 10 ? "0" + n : n;
13521     };
13522     
13523     var m = {
13524         "\b": '\\b',
13525         "\t": '\\t',
13526         "\n": '\\n',
13527         "\f": '\\f',
13528         "\r": '\\r',
13529         '"' : '\\"',
13530         "\\": '\\\\'
13531     };
13532
13533     var encodeString = function(s){
13534         if (/["\\\x00-\x1f]/.test(s)) {
13535             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13536                 var c = m[b];
13537                 if(c){
13538                     return c;
13539                 }
13540                 c = b.charCodeAt();
13541                 return "\\u00" +
13542                     Math.floor(c / 16).toString(16) +
13543                     (c % 16).toString(16);
13544             }) + '"';
13545         }
13546         return '"' + s + '"';
13547     };
13548     
13549     var encodeArray = function(o){
13550         var a = ["["], b, i, l = o.length, v;
13551             for (i = 0; i < l; i += 1) {
13552                 v = o[i];
13553                 switch (typeof v) {
13554                     case "undefined":
13555                     case "function":
13556                     case "unknown":
13557                         break;
13558                     default:
13559                         if (b) {
13560                             a.push(',');
13561                         }
13562                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13563                         b = true;
13564                 }
13565             }
13566             a.push("]");
13567             return a.join("");
13568     };
13569     
13570     var encodeDate = function(o){
13571         return '"' + o.getFullYear() + "-" +
13572                 pad(o.getMonth() + 1) + "-" +
13573                 pad(o.getDate()) + "T" +
13574                 pad(o.getHours()) + ":" +
13575                 pad(o.getMinutes()) + ":" +
13576                 pad(o.getSeconds()) + '"';
13577     };
13578     
13579     /**
13580      * Encodes an Object, Array or other value
13581      * @param {Mixed} o The variable to encode
13582      * @return {String} The JSON string
13583      */
13584     this.encode = function(o)
13585     {
13586         // should this be extended to fully wrap stringify..
13587         
13588         if(typeof o == "undefined" || o === null){
13589             return "null";
13590         }else if(o instanceof Array){
13591             return encodeArray(o);
13592         }else if(o instanceof Date){
13593             return encodeDate(o);
13594         }else if(typeof o == "string"){
13595             return encodeString(o);
13596         }else if(typeof o == "number"){
13597             return isFinite(o) ? String(o) : "null";
13598         }else if(typeof o == "boolean"){
13599             return String(o);
13600         }else {
13601             var a = ["{"], b, i, v;
13602             for (i in o) {
13603                 if(!useHasOwn || o.hasOwnProperty(i)) {
13604                     v = o[i];
13605                     switch (typeof v) {
13606                     case "undefined":
13607                     case "function":
13608                     case "unknown":
13609                         break;
13610                     default:
13611                         if(b){
13612                             a.push(',');
13613                         }
13614                         a.push(this.encode(i), ":",
13615                                 v === null ? "null" : this.encode(v));
13616                         b = true;
13617                     }
13618                 }
13619             }
13620             a.push("}");
13621             return a.join("");
13622         }
13623     };
13624     
13625     /**
13626      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13627      * @param {String} json The JSON string
13628      * @return {Object} The resulting object
13629      */
13630     this.decode = function(json){
13631         
13632         return  /** eval:var:json */ eval("(" + json + ')');
13633     };
13634 })();
13635 /** 
13636  * Shorthand for {@link Roo.util.JSON#encode}
13637  * @member Roo encode 
13638  * @method */
13639 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13640 /** 
13641  * Shorthand for {@link Roo.util.JSON#decode}
13642  * @member Roo decode 
13643  * @method */
13644 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13645 /*
13646  * Based on:
13647  * Ext JS Library 1.1.1
13648  * Copyright(c) 2006-2007, Ext JS, LLC.
13649  *
13650  * Originally Released Under LGPL - original licence link has changed is not relivant.
13651  *
13652  * Fork - LGPL
13653  * <script type="text/javascript">
13654  */
13655  
13656 /**
13657  * @class Roo.util.Format
13658  * Reusable data formatting functions
13659  * @singleton
13660  */
13661 Roo.util.Format = function(){
13662     var trimRe = /^\s+|\s+$/g;
13663     return {
13664         /**
13665          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13666          * @param {String} value The string to truncate
13667          * @param {Number} length The maximum length to allow before truncating
13668          * @return {String} The converted text
13669          */
13670         ellipsis : function(value, len){
13671             if(value && value.length > len){
13672                 return value.substr(0, len-3)+"...";
13673             }
13674             return value;
13675         },
13676
13677         /**
13678          * Checks a reference and converts it to empty string if it is undefined
13679          * @param {Mixed} value Reference to check
13680          * @return {Mixed} Empty string if converted, otherwise the original value
13681          */
13682         undef : function(value){
13683             return typeof value != "undefined" ? value : "";
13684         },
13685
13686         /**
13687          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13688          * @param {String} value The string to encode
13689          * @return {String} The encoded text
13690          */
13691         htmlEncode : function(value){
13692             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13693         },
13694
13695         /**
13696          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13697          * @param {String} value The string to decode
13698          * @return {String} The decoded text
13699          */
13700         htmlDecode : function(value){
13701             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13702         },
13703
13704         /**
13705          * Trims any whitespace from either side of a string
13706          * @param {String} value The text to trim
13707          * @return {String} The trimmed text
13708          */
13709         trim : function(value){
13710             return String(value).replace(trimRe, "");
13711         },
13712
13713         /**
13714          * Returns a substring from within an original string
13715          * @param {String} value The original text
13716          * @param {Number} start The start index of the substring
13717          * @param {Number} length The length of the substring
13718          * @return {String} The substring
13719          */
13720         substr : function(value, start, length){
13721             return String(value).substr(start, length);
13722         },
13723
13724         /**
13725          * Converts a string to all lower case letters
13726          * @param {String} value The text to convert
13727          * @return {String} The converted text
13728          */
13729         lowercase : function(value){
13730             return String(value).toLowerCase();
13731         },
13732
13733         /**
13734          * Converts a string to all upper case letters
13735          * @param {String} value The text to convert
13736          * @return {String} The converted text
13737          */
13738         uppercase : function(value){
13739             return String(value).toUpperCase();
13740         },
13741
13742         /**
13743          * Converts the first character only of a string to upper case
13744          * @param {String} value The text to convert
13745          * @return {String} The converted text
13746          */
13747         capitalize : function(value){
13748             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13749         },
13750
13751         // private
13752         call : function(value, fn){
13753             if(arguments.length > 2){
13754                 var args = Array.prototype.slice.call(arguments, 2);
13755                 args.unshift(value);
13756                  
13757                 return /** eval:var:value */  eval(fn).apply(window, args);
13758             }else{
13759                 /** eval:var:value */
13760                 return /** eval:var:value */ eval(fn).call(window, value);
13761             }
13762         },
13763
13764        
13765         /**
13766          * safer version of Math.toFixed..??/
13767          * @param {Number/String} value The numeric value to format
13768          * @param {Number/String} value Decimal places 
13769          * @return {String} The formatted currency string
13770          */
13771         toFixed : function(v, n)
13772         {
13773             // why not use to fixed - precision is buggered???
13774             if (!n) {
13775                 return Math.round(v-0);
13776             }
13777             var fact = Math.pow(10,n+1);
13778             v = (Math.round((v-0)*fact))/fact;
13779             var z = (''+fact).substring(2);
13780             if (v == Math.floor(v)) {
13781                 return Math.floor(v) + '.' + z;
13782             }
13783             
13784             // now just padd decimals..
13785             var ps = String(v).split('.');
13786             var fd = (ps[1] + z);
13787             var r = fd.substring(0,n); 
13788             var rm = fd.substring(n); 
13789             if (rm < 5) {
13790                 return ps[0] + '.' + r;
13791             }
13792             r*=1; // turn it into a number;
13793             r++;
13794             if (String(r).length != n) {
13795                 ps[0]*=1;
13796                 ps[0]++;
13797                 r = String(r).substring(1); // chop the end off.
13798             }
13799             
13800             return ps[0] + '.' + r;
13801              
13802         },
13803         
13804         /**
13805          * Format a number as US currency
13806          * @param {Number/String} value The numeric value to format
13807          * @return {String} The formatted currency string
13808          */
13809         usMoney : function(v){
13810             return '$' + Roo.util.Format.number(v);
13811         },
13812         
13813         /**
13814          * Format a number
13815          * eventually this should probably emulate php's number_format
13816          * @param {Number/String} value The numeric value to format
13817          * @param {Number} decimals number of decimal places
13818          * @param {String} delimiter for thousands (default comma)
13819          * @return {String} The formatted currency string
13820          */
13821         number : function(v, decimals, thousandsDelimiter)
13822         {
13823             // multiply and round.
13824             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13825             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13826             
13827             var mul = Math.pow(10, decimals);
13828             var zero = String(mul).substring(1);
13829             v = (Math.round((v-0)*mul))/mul;
13830             
13831             // if it's '0' number.. then
13832             
13833             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13834             v = String(v);
13835             var ps = v.split('.');
13836             var whole = ps[0];
13837             
13838             var r = /(\d+)(\d{3})/;
13839             // add comma's
13840             
13841             if(thousandsDelimiter.length != 0) {
13842                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13843             } 
13844             
13845             var sub = ps[1] ?
13846                     // has decimals..
13847                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13848                     // does not have decimals
13849                     (decimals ? ('.' + zero) : '');
13850             
13851             
13852             return whole + sub ;
13853         },
13854         
13855         /**
13856          * Parse a value into a formatted date using the specified format pattern.
13857          * @param {Mixed} value The value to format
13858          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13859          * @return {String} The formatted date string
13860          */
13861         date : function(v, format){
13862             if(!v){
13863                 return "";
13864             }
13865             if(!(v instanceof Date)){
13866                 v = new Date(Date.parse(v));
13867             }
13868             return v.dateFormat(format || Roo.util.Format.defaults.date);
13869         },
13870
13871         /**
13872          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13873          * @param {String} format Any valid date format string
13874          * @return {Function} The date formatting function
13875          */
13876         dateRenderer : function(format){
13877             return function(v){
13878                 return Roo.util.Format.date(v, format);  
13879             };
13880         },
13881
13882         // private
13883         stripTagsRE : /<\/?[^>]+>/gi,
13884         
13885         /**
13886          * Strips all HTML tags
13887          * @param {Mixed} value The text from which to strip tags
13888          * @return {String} The stripped text
13889          */
13890         stripTags : function(v){
13891             return !v ? v : String(v).replace(this.stripTagsRE, "");
13892         }
13893     };
13894 }();
13895 Roo.util.Format.defaults = {
13896     date : 'd/M/Y'
13897 };/*
13898  * Based on:
13899  * Ext JS Library 1.1.1
13900  * Copyright(c) 2006-2007, Ext JS, LLC.
13901  *
13902  * Originally Released Under LGPL - original licence link has changed is not relivant.
13903  *
13904  * Fork - LGPL
13905  * <script type="text/javascript">
13906  */
13907
13908
13909  
13910
13911 /**
13912  * @class Roo.MasterTemplate
13913  * @extends Roo.Template
13914  * Provides a template that can have child templates. The syntax is:
13915 <pre><code>
13916 var t = new Roo.MasterTemplate(
13917         '&lt;select name="{name}"&gt;',
13918                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13919         '&lt;/select&gt;'
13920 );
13921 t.add('options', {value: 'foo', text: 'bar'});
13922 // or you can add multiple child elements in one shot
13923 t.addAll('options', [
13924     {value: 'foo', text: 'bar'},
13925     {value: 'foo2', text: 'bar2'},
13926     {value: 'foo3', text: 'bar3'}
13927 ]);
13928 // then append, applying the master template values
13929 t.append('my-form', {name: 'my-select'});
13930 </code></pre>
13931 * A name attribute for the child template is not required if you have only one child
13932 * template or you want to refer to them by index.
13933  */
13934 Roo.MasterTemplate = function(){
13935     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13936     this.originalHtml = this.html;
13937     var st = {};
13938     var m, re = this.subTemplateRe;
13939     re.lastIndex = 0;
13940     var subIndex = 0;
13941     while(m = re.exec(this.html)){
13942         var name = m[1], content = m[2];
13943         st[subIndex] = {
13944             name: name,
13945             index: subIndex,
13946             buffer: [],
13947             tpl : new Roo.Template(content)
13948         };
13949         if(name){
13950             st[name] = st[subIndex];
13951         }
13952         st[subIndex].tpl.compile();
13953         st[subIndex].tpl.call = this.call.createDelegate(this);
13954         subIndex++;
13955     }
13956     this.subCount = subIndex;
13957     this.subs = st;
13958 };
13959 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13960     /**
13961     * The regular expression used to match sub templates
13962     * @type RegExp
13963     * @property
13964     */
13965     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13966
13967     /**
13968      * Applies the passed values to a child template.
13969      * @param {String/Number} name (optional) The name or index of the child template
13970      * @param {Array/Object} values The values to be applied to the template
13971      * @return {MasterTemplate} this
13972      */
13973      add : function(name, values){
13974         if(arguments.length == 1){
13975             values = arguments[0];
13976             name = 0;
13977         }
13978         var s = this.subs[name];
13979         s.buffer[s.buffer.length] = s.tpl.apply(values);
13980         return this;
13981     },
13982
13983     /**
13984      * Applies all the passed values to a child template.
13985      * @param {String/Number} name (optional) The name or index of the child template
13986      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13987      * @param {Boolean} reset (optional) True to reset the template first
13988      * @return {MasterTemplate} this
13989      */
13990     fill : function(name, values, reset){
13991         var a = arguments;
13992         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13993             values = a[0];
13994             name = 0;
13995             reset = a[1];
13996         }
13997         if(reset){
13998             this.reset();
13999         }
14000         for(var i = 0, len = values.length; i < len; i++){
14001             this.add(name, values[i]);
14002         }
14003         return this;
14004     },
14005
14006     /**
14007      * Resets the template for reuse
14008      * @return {MasterTemplate} this
14009      */
14010      reset : function(){
14011         var s = this.subs;
14012         for(var i = 0; i < this.subCount; i++){
14013             s[i].buffer = [];
14014         }
14015         return this;
14016     },
14017
14018     applyTemplate : function(values){
14019         var s = this.subs;
14020         var replaceIndex = -1;
14021         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14022             return s[++replaceIndex].buffer.join("");
14023         });
14024         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14025     },
14026
14027     apply : function(){
14028         return this.applyTemplate.apply(this, arguments);
14029     },
14030
14031     compile : function(){return this;}
14032 });
14033
14034 /**
14035  * Alias for fill().
14036  * @method
14037  */
14038 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14039  /**
14040  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14041  * var tpl = Roo.MasterTemplate.from('element-id');
14042  * @param {String/HTMLElement} el
14043  * @param {Object} config
14044  * @static
14045  */
14046 Roo.MasterTemplate.from = function(el, config){
14047     el = Roo.getDom(el);
14048     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14049 };/*
14050  * Based on:
14051  * Ext JS Library 1.1.1
14052  * Copyright(c) 2006-2007, Ext JS, LLC.
14053  *
14054  * Originally Released Under LGPL - original licence link has changed is not relivant.
14055  *
14056  * Fork - LGPL
14057  * <script type="text/javascript">
14058  */
14059
14060  
14061 /**
14062  * @class Roo.util.CSS
14063  * Utility class for manipulating CSS rules
14064  * @singleton
14065  */
14066 Roo.util.CSS = function(){
14067         var rules = null;
14068         var doc = document;
14069
14070     var camelRe = /(-[a-z])/gi;
14071     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14072
14073    return {
14074    /**
14075     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14076     * tag and appended to the HEAD of the document.
14077     * @param {String|Object} cssText The text containing the css rules
14078     * @param {String} id An id to add to the stylesheet for later removal
14079     * @return {StyleSheet}
14080     */
14081     createStyleSheet : function(cssText, id){
14082         var ss;
14083         var head = doc.getElementsByTagName("head")[0];
14084         var nrules = doc.createElement("style");
14085         nrules.setAttribute("type", "text/css");
14086         if(id){
14087             nrules.setAttribute("id", id);
14088         }
14089         if (typeof(cssText) != 'string') {
14090             // support object maps..
14091             // not sure if this a good idea.. 
14092             // perhaps it should be merged with the general css handling
14093             // and handle js style props.
14094             var cssTextNew = [];
14095             for(var n in cssText) {
14096                 var citems = [];
14097                 for(var k in cssText[n]) {
14098                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14099                 }
14100                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14101                 
14102             }
14103             cssText = cssTextNew.join("\n");
14104             
14105         }
14106        
14107        
14108        if(Roo.isIE){
14109            head.appendChild(nrules);
14110            ss = nrules.styleSheet;
14111            ss.cssText = cssText;
14112        }else{
14113            try{
14114                 nrules.appendChild(doc.createTextNode(cssText));
14115            }catch(e){
14116                nrules.cssText = cssText; 
14117            }
14118            head.appendChild(nrules);
14119            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14120        }
14121        this.cacheStyleSheet(ss);
14122        return ss;
14123    },
14124
14125    /**
14126     * Removes a style or link tag by id
14127     * @param {String} id The id of the tag
14128     */
14129    removeStyleSheet : function(id){
14130        var existing = doc.getElementById(id);
14131        if(existing){
14132            existing.parentNode.removeChild(existing);
14133        }
14134    },
14135
14136    /**
14137     * Dynamically swaps an existing stylesheet reference for a new one
14138     * @param {String} id The id of an existing link tag to remove
14139     * @param {String} url The href of the new stylesheet to include
14140     */
14141    swapStyleSheet : function(id, url){
14142        this.removeStyleSheet(id);
14143        var ss = doc.createElement("link");
14144        ss.setAttribute("rel", "stylesheet");
14145        ss.setAttribute("type", "text/css");
14146        ss.setAttribute("id", id);
14147        ss.setAttribute("href", url);
14148        doc.getElementsByTagName("head")[0].appendChild(ss);
14149    },
14150    
14151    /**
14152     * Refresh the rule cache if you have dynamically added stylesheets
14153     * @return {Object} An object (hash) of rules indexed by selector
14154     */
14155    refreshCache : function(){
14156        return this.getRules(true);
14157    },
14158
14159    // private
14160    cacheStyleSheet : function(stylesheet){
14161        if(!rules){
14162            rules = {};
14163        }
14164        try{// try catch for cross domain access issue
14165            var ssRules = stylesheet.cssRules || stylesheet.rules;
14166            for(var j = ssRules.length-1; j >= 0; --j){
14167                rules[ssRules[j].selectorText] = ssRules[j];
14168            }
14169        }catch(e){}
14170    },
14171    
14172    /**
14173     * Gets all css rules for the document
14174     * @param {Boolean} refreshCache true to refresh the internal cache
14175     * @return {Object} An object (hash) of rules indexed by selector
14176     */
14177    getRules : function(refreshCache){
14178                 if(rules == null || refreshCache){
14179                         rules = {};
14180                         var ds = doc.styleSheets;
14181                         for(var i =0, len = ds.length; i < len; i++){
14182                             try{
14183                         this.cacheStyleSheet(ds[i]);
14184                     }catch(e){} 
14185                 }
14186                 }
14187                 return rules;
14188         },
14189         
14190         /**
14191     * Gets an an individual CSS rule by selector(s)
14192     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14193     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14194     * @return {CSSRule} The CSS rule or null if one is not found
14195     */
14196    getRule : function(selector, refreshCache){
14197                 var rs = this.getRules(refreshCache);
14198                 if(!(selector instanceof Array)){
14199                     return rs[selector];
14200                 }
14201                 for(var i = 0; i < selector.length; i++){
14202                         if(rs[selector[i]]){
14203                                 return rs[selector[i]];
14204                         }
14205                 }
14206                 return null;
14207         },
14208         
14209         
14210         /**
14211     * Updates a rule property
14212     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14213     * @param {String} property The css property
14214     * @param {String} value The new value for the property
14215     * @return {Boolean} true If a rule was found and updated
14216     */
14217    updateRule : function(selector, property, value){
14218                 if(!(selector instanceof Array)){
14219                         var rule = this.getRule(selector);
14220                         if(rule){
14221                                 rule.style[property.replace(camelRe, camelFn)] = value;
14222                                 return true;
14223                         }
14224                 }else{
14225                         for(var i = 0; i < selector.length; i++){
14226                                 if(this.updateRule(selector[i], property, value)){
14227                                         return true;
14228                                 }
14229                         }
14230                 }
14231                 return false;
14232         }
14233    };   
14234 }();/*
14235  * Based on:
14236  * Ext JS Library 1.1.1
14237  * Copyright(c) 2006-2007, Ext JS, LLC.
14238  *
14239  * Originally Released Under LGPL - original licence link has changed is not relivant.
14240  *
14241  * Fork - LGPL
14242  * <script type="text/javascript">
14243  */
14244
14245  
14246
14247 /**
14248  * @class Roo.util.ClickRepeater
14249  * @extends Roo.util.Observable
14250  * 
14251  * A wrapper class which can be applied to any element. Fires a "click" event while the
14252  * mouse is pressed. The interval between firings may be specified in the config but
14253  * defaults to 10 milliseconds.
14254  * 
14255  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14256  * 
14257  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14258  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14259  * Similar to an autorepeat key delay.
14260  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14261  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14262  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14263  *           "interval" and "delay" are ignored. "immediate" is honored.
14264  * @cfg {Boolean} preventDefault True to prevent the default click event
14265  * @cfg {Boolean} stopDefault True to stop the default click event
14266  * 
14267  * @history
14268  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14269  *     2007-02-02 jvs Renamed to ClickRepeater
14270  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14271  *
14272  *  @constructor
14273  * @param {String/HTMLElement/Element} el The element to listen on
14274  * @param {Object} config
14275  **/
14276 Roo.util.ClickRepeater = function(el, config)
14277 {
14278     this.el = Roo.get(el);
14279     this.el.unselectable();
14280
14281     Roo.apply(this, config);
14282
14283     this.addEvents({
14284     /**
14285      * @event mousedown
14286      * Fires when the mouse button is depressed.
14287      * @param {Roo.util.ClickRepeater} this
14288      */
14289         "mousedown" : true,
14290     /**
14291      * @event click
14292      * Fires on a specified interval during the time the element is pressed.
14293      * @param {Roo.util.ClickRepeater} this
14294      */
14295         "click" : true,
14296     /**
14297      * @event mouseup
14298      * Fires when the mouse key is released.
14299      * @param {Roo.util.ClickRepeater} this
14300      */
14301         "mouseup" : true
14302     });
14303
14304     this.el.on("mousedown", this.handleMouseDown, this);
14305     if(this.preventDefault || this.stopDefault){
14306         this.el.on("click", function(e){
14307             if(this.preventDefault){
14308                 e.preventDefault();
14309             }
14310             if(this.stopDefault){
14311                 e.stopEvent();
14312             }
14313         }, this);
14314     }
14315
14316     // allow inline handler
14317     if(this.handler){
14318         this.on("click", this.handler,  this.scope || this);
14319     }
14320
14321     Roo.util.ClickRepeater.superclass.constructor.call(this);
14322 };
14323
14324 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14325     interval : 20,
14326     delay: 250,
14327     preventDefault : true,
14328     stopDefault : false,
14329     timer : 0,
14330
14331     // private
14332     handleMouseDown : function(){
14333         clearTimeout(this.timer);
14334         this.el.blur();
14335         if(this.pressClass){
14336             this.el.addClass(this.pressClass);
14337         }
14338         this.mousedownTime = new Date();
14339
14340         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14341         this.el.on("mouseout", this.handleMouseOut, this);
14342
14343         this.fireEvent("mousedown", this);
14344         this.fireEvent("click", this);
14345         
14346         this.timer = this.click.defer(this.delay || this.interval, this);
14347     },
14348
14349     // private
14350     click : function(){
14351         this.fireEvent("click", this);
14352         this.timer = this.click.defer(this.getInterval(), this);
14353     },
14354
14355     // private
14356     getInterval: function(){
14357         if(!this.accelerate){
14358             return this.interval;
14359         }
14360         var pressTime = this.mousedownTime.getElapsed();
14361         if(pressTime < 500){
14362             return 400;
14363         }else if(pressTime < 1700){
14364             return 320;
14365         }else if(pressTime < 2600){
14366             return 250;
14367         }else if(pressTime < 3500){
14368             return 180;
14369         }else if(pressTime < 4400){
14370             return 140;
14371         }else if(pressTime < 5300){
14372             return 80;
14373         }else if(pressTime < 6200){
14374             return 50;
14375         }else{
14376             return 10;
14377         }
14378     },
14379
14380     // private
14381     handleMouseOut : function(){
14382         clearTimeout(this.timer);
14383         if(this.pressClass){
14384             this.el.removeClass(this.pressClass);
14385         }
14386         this.el.on("mouseover", this.handleMouseReturn, this);
14387     },
14388
14389     // private
14390     handleMouseReturn : function(){
14391         this.el.un("mouseover", this.handleMouseReturn);
14392         if(this.pressClass){
14393             this.el.addClass(this.pressClass);
14394         }
14395         this.click();
14396     },
14397
14398     // private
14399     handleMouseUp : function(){
14400         clearTimeout(this.timer);
14401         this.el.un("mouseover", this.handleMouseReturn);
14402         this.el.un("mouseout", this.handleMouseOut);
14403         Roo.get(document).un("mouseup", this.handleMouseUp);
14404         this.el.removeClass(this.pressClass);
14405         this.fireEvent("mouseup", this);
14406     }
14407 });/*
14408  * Based on:
14409  * Ext JS Library 1.1.1
14410  * Copyright(c) 2006-2007, Ext JS, LLC.
14411  *
14412  * Originally Released Under LGPL - original licence link has changed is not relivant.
14413  *
14414  * Fork - LGPL
14415  * <script type="text/javascript">
14416  */
14417
14418  
14419 /**
14420  * @class Roo.KeyNav
14421  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14422  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14423  * way to implement custom navigation schemes for any UI component.</p>
14424  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14425  * pageUp, pageDown, del, home, end.  Usage:</p>
14426  <pre><code>
14427 var nav = new Roo.KeyNav("my-element", {
14428     "left" : function(e){
14429         this.moveLeft(e.ctrlKey);
14430     },
14431     "right" : function(e){
14432         this.moveRight(e.ctrlKey);
14433     },
14434     "enter" : function(e){
14435         this.save();
14436     },
14437     scope : this
14438 });
14439 </code></pre>
14440  * @constructor
14441  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14442  * @param {Object} config The config
14443  */
14444 Roo.KeyNav = function(el, config){
14445     this.el = Roo.get(el);
14446     Roo.apply(this, config);
14447     if(!this.disabled){
14448         this.disabled = true;
14449         this.enable();
14450     }
14451 };
14452
14453 Roo.KeyNav.prototype = {
14454     /**
14455      * @cfg {Boolean} disabled
14456      * True to disable this KeyNav instance (defaults to false)
14457      */
14458     disabled : false,
14459     /**
14460      * @cfg {String} defaultEventAction
14461      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14462      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14463      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14464      */
14465     defaultEventAction: "stopEvent",
14466     /**
14467      * @cfg {Boolean} forceKeyDown
14468      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14469      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14470      * handle keydown instead of keypress.
14471      */
14472     forceKeyDown : false,
14473
14474     // private
14475     prepareEvent : function(e){
14476         var k = e.getKey();
14477         var h = this.keyToHandler[k];
14478         //if(h && this[h]){
14479         //    e.stopPropagation();
14480         //}
14481         if(Roo.isSafari && h && k >= 37 && k <= 40){
14482             e.stopEvent();
14483         }
14484     },
14485
14486     // private
14487     relay : function(e){
14488         var k = e.getKey();
14489         var h = this.keyToHandler[k];
14490         if(h && this[h]){
14491             if(this.doRelay(e, this[h], h) !== true){
14492                 e[this.defaultEventAction]();
14493             }
14494         }
14495     },
14496
14497     // private
14498     doRelay : function(e, h, hname){
14499         return h.call(this.scope || this, e);
14500     },
14501
14502     // possible handlers
14503     enter : false,
14504     left : false,
14505     right : false,
14506     up : false,
14507     down : false,
14508     tab : false,
14509     esc : false,
14510     pageUp : false,
14511     pageDown : false,
14512     del : false,
14513     home : false,
14514     end : false,
14515
14516     // quick lookup hash
14517     keyToHandler : {
14518         37 : "left",
14519         39 : "right",
14520         38 : "up",
14521         40 : "down",
14522         33 : "pageUp",
14523         34 : "pageDown",
14524         46 : "del",
14525         36 : "home",
14526         35 : "end",
14527         13 : "enter",
14528         27 : "esc",
14529         9  : "tab"
14530     },
14531
14532         /**
14533          * Enable this KeyNav
14534          */
14535         enable: function(){
14536                 if(this.disabled){
14537             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14538             // the EventObject will normalize Safari automatically
14539             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14540                 this.el.on("keydown", this.relay,  this);
14541             }else{
14542                 this.el.on("keydown", this.prepareEvent,  this);
14543                 this.el.on("keypress", this.relay,  this);
14544             }
14545                     this.disabled = false;
14546                 }
14547         },
14548
14549         /**
14550          * Disable this KeyNav
14551          */
14552         disable: function(){
14553                 if(!this.disabled){
14554                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14555                 this.el.un("keydown", this.relay);
14556             }else{
14557                 this.el.un("keydown", this.prepareEvent);
14558                 this.el.un("keypress", this.relay);
14559             }
14560                     this.disabled = true;
14561                 }
14562         }
14563 };/*
14564  * Based on:
14565  * Ext JS Library 1.1.1
14566  * Copyright(c) 2006-2007, Ext JS, LLC.
14567  *
14568  * Originally Released Under LGPL - original licence link has changed is not relivant.
14569  *
14570  * Fork - LGPL
14571  * <script type="text/javascript">
14572  */
14573
14574  
14575 /**
14576  * @class Roo.KeyMap
14577  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14578  * The constructor accepts the same config object as defined by {@link #addBinding}.
14579  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14580  * combination it will call the function with this signature (if the match is a multi-key
14581  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14582  * A KeyMap can also handle a string representation of keys.<br />
14583  * Usage:
14584  <pre><code>
14585 // map one key by key code
14586 var map = new Roo.KeyMap("my-element", {
14587     key: 13, // or Roo.EventObject.ENTER
14588     fn: myHandler,
14589     scope: myObject
14590 });
14591
14592 // map multiple keys to one action by string
14593 var map = new Roo.KeyMap("my-element", {
14594     key: "a\r\n\t",
14595     fn: myHandler,
14596     scope: myObject
14597 });
14598
14599 // map multiple keys to multiple actions by strings and array of codes
14600 var map = new Roo.KeyMap("my-element", [
14601     {
14602         key: [10,13],
14603         fn: function(){ alert("Return was pressed"); }
14604     }, {
14605         key: "abc",
14606         fn: function(){ alert('a, b or c was pressed'); }
14607     }, {
14608         key: "\t",
14609         ctrl:true,
14610         shift:true,
14611         fn: function(){ alert('Control + shift + tab was pressed.'); }
14612     }
14613 ]);
14614 </code></pre>
14615  * <b>Note: A KeyMap starts enabled</b>
14616  * @constructor
14617  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14618  * @param {Object} config The config (see {@link #addBinding})
14619  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14620  */
14621 Roo.KeyMap = function(el, config, eventName){
14622     this.el  = Roo.get(el);
14623     this.eventName = eventName || "keydown";
14624     this.bindings = [];
14625     if(config){
14626         this.addBinding(config);
14627     }
14628     this.enable();
14629 };
14630
14631 Roo.KeyMap.prototype = {
14632     /**
14633      * True to stop the event from bubbling and prevent the default browser action if the
14634      * key was handled by the KeyMap (defaults to false)
14635      * @type Boolean
14636      */
14637     stopEvent : false,
14638
14639     /**
14640      * Add a new binding to this KeyMap. The following config object properties are supported:
14641      * <pre>
14642 Property    Type             Description
14643 ----------  ---------------  ----------------------------------------------------------------------
14644 key         String/Array     A single keycode or an array of keycodes to handle
14645 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14646 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14647 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14648 fn          Function         The function to call when KeyMap finds the expected key combination
14649 scope       Object           The scope of the callback function
14650 </pre>
14651      *
14652      * Usage:
14653      * <pre><code>
14654 // Create a KeyMap
14655 var map = new Roo.KeyMap(document, {
14656     key: Roo.EventObject.ENTER,
14657     fn: handleKey,
14658     scope: this
14659 });
14660
14661 //Add a new binding to the existing KeyMap later
14662 map.addBinding({
14663     key: 'abc',
14664     shift: true,
14665     fn: handleKey,
14666     scope: this
14667 });
14668 </code></pre>
14669      * @param {Object/Array} config A single KeyMap config or an array of configs
14670      */
14671         addBinding : function(config){
14672         if(config instanceof Array){
14673             for(var i = 0, len = config.length; i < len; i++){
14674                 this.addBinding(config[i]);
14675             }
14676             return;
14677         }
14678         var keyCode = config.key,
14679             shift = config.shift, 
14680             ctrl = config.ctrl, 
14681             alt = config.alt,
14682             fn = config.fn,
14683             scope = config.scope;
14684         if(typeof keyCode == "string"){
14685             var ks = [];
14686             var keyString = keyCode.toUpperCase();
14687             for(var j = 0, len = keyString.length; j < len; j++){
14688                 ks.push(keyString.charCodeAt(j));
14689             }
14690             keyCode = ks;
14691         }
14692         var keyArray = keyCode instanceof Array;
14693         var handler = function(e){
14694             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14695                 var k = e.getKey();
14696                 if(keyArray){
14697                     for(var i = 0, len = keyCode.length; i < len; i++){
14698                         if(keyCode[i] == k){
14699                           if(this.stopEvent){
14700                               e.stopEvent();
14701                           }
14702                           fn.call(scope || window, k, e);
14703                           return;
14704                         }
14705                     }
14706                 }else{
14707                     if(k == keyCode){
14708                         if(this.stopEvent){
14709                            e.stopEvent();
14710                         }
14711                         fn.call(scope || window, k, e);
14712                     }
14713                 }
14714             }
14715         };
14716         this.bindings.push(handler);  
14717         },
14718
14719     /**
14720      * Shorthand for adding a single key listener
14721      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14722      * following options:
14723      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14724      * @param {Function} fn The function to call
14725      * @param {Object} scope (optional) The scope of the function
14726      */
14727     on : function(key, fn, scope){
14728         var keyCode, shift, ctrl, alt;
14729         if(typeof key == "object" && !(key instanceof Array)){
14730             keyCode = key.key;
14731             shift = key.shift;
14732             ctrl = key.ctrl;
14733             alt = key.alt;
14734         }else{
14735             keyCode = key;
14736         }
14737         this.addBinding({
14738             key: keyCode,
14739             shift: shift,
14740             ctrl: ctrl,
14741             alt: alt,
14742             fn: fn,
14743             scope: scope
14744         })
14745     },
14746
14747     // private
14748     handleKeyDown : function(e){
14749             if(this.enabled){ //just in case
14750             var b = this.bindings;
14751             for(var i = 0, len = b.length; i < len; i++){
14752                 b[i].call(this, e);
14753             }
14754             }
14755         },
14756         
14757         /**
14758          * Returns true if this KeyMap is enabled
14759          * @return {Boolean} 
14760          */
14761         isEnabled : function(){
14762             return this.enabled;  
14763         },
14764         
14765         /**
14766          * Enables this KeyMap
14767          */
14768         enable: function(){
14769                 if(!this.enabled){
14770                     this.el.on(this.eventName, this.handleKeyDown, this);
14771                     this.enabled = true;
14772                 }
14773         },
14774
14775         /**
14776          * Disable this KeyMap
14777          */
14778         disable: function(){
14779                 if(this.enabled){
14780                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14781                     this.enabled = false;
14782                 }
14783         }
14784 };/*
14785  * Based on:
14786  * Ext JS Library 1.1.1
14787  * Copyright(c) 2006-2007, Ext JS, LLC.
14788  *
14789  * Originally Released Under LGPL - original licence link has changed is not relivant.
14790  *
14791  * Fork - LGPL
14792  * <script type="text/javascript">
14793  */
14794
14795  
14796 /**
14797  * @class Roo.util.TextMetrics
14798  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14799  * wide, in pixels, a given block of text will be.
14800  * @singleton
14801  */
14802 Roo.util.TextMetrics = function(){
14803     var shared;
14804     return {
14805         /**
14806          * Measures the size of the specified text
14807          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14808          * that can affect the size of the rendered text
14809          * @param {String} text The text to measure
14810          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14811          * in order to accurately measure the text height
14812          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14813          */
14814         measure : function(el, text, fixedWidth){
14815             if(!shared){
14816                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14817             }
14818             shared.bind(el);
14819             shared.setFixedWidth(fixedWidth || 'auto');
14820             return shared.getSize(text);
14821         },
14822
14823         /**
14824          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14825          * the overhead of multiple calls to initialize the style properties on each measurement.
14826          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14827          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14828          * in order to accurately measure the text height
14829          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14830          */
14831         createInstance : function(el, fixedWidth){
14832             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14833         }
14834     };
14835 }();
14836
14837  
14838
14839 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14840     var ml = new Roo.Element(document.createElement('div'));
14841     document.body.appendChild(ml.dom);
14842     ml.position('absolute');
14843     ml.setLeftTop(-1000, -1000);
14844     ml.hide();
14845
14846     if(fixedWidth){
14847         ml.setWidth(fixedWidth);
14848     }
14849      
14850     var instance = {
14851         /**
14852          * Returns the size of the specified text based on the internal element's style and width properties
14853          * @memberOf Roo.util.TextMetrics.Instance#
14854          * @param {String} text The text to measure
14855          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14856          */
14857         getSize : function(text){
14858             ml.update(text);
14859             var s = ml.getSize();
14860             ml.update('');
14861             return s;
14862         },
14863
14864         /**
14865          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14866          * that can affect the size of the rendered text
14867          * @memberOf Roo.util.TextMetrics.Instance#
14868          * @param {String/HTMLElement} el The element, dom node or id
14869          */
14870         bind : function(el){
14871             ml.setStyle(
14872                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14873             );
14874         },
14875
14876         /**
14877          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14878          * to set a fixed width in order to accurately measure the text height.
14879          * @memberOf Roo.util.TextMetrics.Instance#
14880          * @param {Number} width The width to set on the element
14881          */
14882         setFixedWidth : function(width){
14883             ml.setWidth(width);
14884         },
14885
14886         /**
14887          * Returns the measured width of the specified text
14888          * @memberOf Roo.util.TextMetrics.Instance#
14889          * @param {String} text The text to measure
14890          * @return {Number} width The width in pixels
14891          */
14892         getWidth : function(text){
14893             ml.dom.style.width = 'auto';
14894             return this.getSize(text).width;
14895         },
14896
14897         /**
14898          * Returns the measured height of the specified text.  For multiline text, be sure to call
14899          * {@link #setFixedWidth} if necessary.
14900          * @memberOf Roo.util.TextMetrics.Instance#
14901          * @param {String} text The text to measure
14902          * @return {Number} height The height in pixels
14903          */
14904         getHeight : function(text){
14905             return this.getSize(text).height;
14906         }
14907     };
14908
14909     instance.bind(bindTo);
14910
14911     return instance;
14912 };
14913
14914 // backwards compat
14915 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14916  * Based on:
14917  * Ext JS Library 1.1.1
14918  * Copyright(c) 2006-2007, Ext JS, LLC.
14919  *
14920  * Originally Released Under LGPL - original licence link has changed is not relivant.
14921  *
14922  * Fork - LGPL
14923  * <script type="text/javascript">
14924  */
14925
14926 /**
14927  * @class Roo.state.Provider
14928  * Abstract base class for state provider implementations. This class provides methods
14929  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14930  * Provider interface.
14931  */
14932 Roo.state.Provider = function(){
14933     /**
14934      * @event statechange
14935      * Fires when a state change occurs.
14936      * @param {Provider} this This state provider
14937      * @param {String} key The state key which was changed
14938      * @param {String} value The encoded value for the state
14939      */
14940     this.addEvents({
14941         "statechange": true
14942     });
14943     this.state = {};
14944     Roo.state.Provider.superclass.constructor.call(this);
14945 };
14946 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14947     /**
14948      * Returns the current value for a key
14949      * @param {String} name The key name
14950      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14951      * @return {Mixed} The state data
14952      */
14953     get : function(name, defaultValue){
14954         return typeof this.state[name] == "undefined" ?
14955             defaultValue : this.state[name];
14956     },
14957     
14958     /**
14959      * Clears a value from the state
14960      * @param {String} name The key name
14961      */
14962     clear : function(name){
14963         delete this.state[name];
14964         this.fireEvent("statechange", this, name, null);
14965     },
14966     
14967     /**
14968      * Sets the value for a key
14969      * @param {String} name The key name
14970      * @param {Mixed} value The value to set
14971      */
14972     set : function(name, value){
14973         this.state[name] = value;
14974         this.fireEvent("statechange", this, name, value);
14975     },
14976     
14977     /**
14978      * Decodes a string previously encoded with {@link #encodeValue}.
14979      * @param {String} value The value to decode
14980      * @return {Mixed} The decoded value
14981      */
14982     decodeValue : function(cookie){
14983         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14984         var matches = re.exec(unescape(cookie));
14985         if(!matches || !matches[1]) {
14986             return; // non state cookie
14987         }
14988         var type = matches[1];
14989         var v = matches[2];
14990         switch(type){
14991             case "n":
14992                 return parseFloat(v);
14993             case "d":
14994                 return new Date(Date.parse(v));
14995             case "b":
14996                 return (v == "1");
14997             case "a":
14998                 var all = [];
14999                 var values = v.split("^");
15000                 for(var i = 0, len = values.length; i < len; i++){
15001                     all.push(this.decodeValue(values[i]));
15002                 }
15003                 return all;
15004            case "o":
15005                 var all = {};
15006                 var values = v.split("^");
15007                 for(var i = 0, len = values.length; i < len; i++){
15008                     var kv = values[i].split("=");
15009                     all[kv[0]] = this.decodeValue(kv[1]);
15010                 }
15011                 return all;
15012            default:
15013                 return v;
15014         }
15015     },
15016     
15017     /**
15018      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15019      * @param {Mixed} value The value to encode
15020      * @return {String} The encoded value
15021      */
15022     encodeValue : function(v){
15023         var enc;
15024         if(typeof v == "number"){
15025             enc = "n:" + v;
15026         }else if(typeof v == "boolean"){
15027             enc = "b:" + (v ? "1" : "0");
15028         }else if(v instanceof Date){
15029             enc = "d:" + v.toGMTString();
15030         }else if(v instanceof Array){
15031             var flat = "";
15032             for(var i = 0, len = v.length; i < len; i++){
15033                 flat += this.encodeValue(v[i]);
15034                 if(i != len-1) {
15035                     flat += "^";
15036                 }
15037             }
15038             enc = "a:" + flat;
15039         }else if(typeof v == "object"){
15040             var flat = "";
15041             for(var key in v){
15042                 if(typeof v[key] != "function"){
15043                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15044                 }
15045             }
15046             enc = "o:" + flat.substring(0, flat.length-1);
15047         }else{
15048             enc = "s:" + v;
15049         }
15050         return escape(enc);        
15051     }
15052 });
15053
15054 /*
15055  * Based on:
15056  * Ext JS Library 1.1.1
15057  * Copyright(c) 2006-2007, Ext JS, LLC.
15058  *
15059  * Originally Released Under LGPL - original licence link has changed is not relivant.
15060  *
15061  * Fork - LGPL
15062  * <script type="text/javascript">
15063  */
15064 /**
15065  * @class Roo.state.Manager
15066  * This is the global state manager. By default all components that are "state aware" check this class
15067  * for state information if you don't pass them a custom state provider. In order for this class
15068  * to be useful, it must be initialized with a provider when your application initializes.
15069  <pre><code>
15070 // in your initialization function
15071 init : function(){
15072    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15073    ...
15074    // supposed you have a {@link Roo.BorderLayout}
15075    var layout = new Roo.BorderLayout(...);
15076    layout.restoreState();
15077    // or a {Roo.BasicDialog}
15078    var dialog = new Roo.BasicDialog(...);
15079    dialog.restoreState();
15080  </code></pre>
15081  * @singleton
15082  */
15083 Roo.state.Manager = function(){
15084     var provider = new Roo.state.Provider();
15085     
15086     return {
15087         /**
15088          * Configures the default state provider for your application
15089          * @param {Provider} stateProvider The state provider to set
15090          */
15091         setProvider : function(stateProvider){
15092             provider = stateProvider;
15093         },
15094         
15095         /**
15096          * Returns the current value for a key
15097          * @param {String} name The key name
15098          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15099          * @return {Mixed} The state data
15100          */
15101         get : function(key, defaultValue){
15102             return provider.get(key, defaultValue);
15103         },
15104         
15105         /**
15106          * Sets the value for a key
15107          * @param {String} name The key name
15108          * @param {Mixed} value The state data
15109          */
15110          set : function(key, value){
15111             provider.set(key, value);
15112         },
15113         
15114         /**
15115          * Clears a value from the state
15116          * @param {String} name The key name
15117          */
15118         clear : function(key){
15119             provider.clear(key);
15120         },
15121         
15122         /**
15123          * Gets the currently configured state provider
15124          * @return {Provider} The state provider
15125          */
15126         getProvider : function(){
15127             return provider;
15128         }
15129     };
15130 }();
15131 /*
15132  * Based on:
15133  * Ext JS Library 1.1.1
15134  * Copyright(c) 2006-2007, Ext JS, LLC.
15135  *
15136  * Originally Released Under LGPL - original licence link has changed is not relivant.
15137  *
15138  * Fork - LGPL
15139  * <script type="text/javascript">
15140  */
15141 /**
15142  * @class Roo.state.CookieProvider
15143  * @extends Roo.state.Provider
15144  * The default Provider implementation which saves state via cookies.
15145  * <br />Usage:
15146  <pre><code>
15147    var cp = new Roo.state.CookieProvider({
15148        path: "/cgi-bin/",
15149        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15150        domain: "roojs.com"
15151    })
15152    Roo.state.Manager.setProvider(cp);
15153  </code></pre>
15154  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15155  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15156  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15157  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15158  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15159  * domain the page is running on including the 'www' like 'www.roojs.com')
15160  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15161  * @constructor
15162  * Create a new CookieProvider
15163  * @param {Object} config The configuration object
15164  */
15165 Roo.state.CookieProvider = function(config){
15166     Roo.state.CookieProvider.superclass.constructor.call(this);
15167     this.path = "/";
15168     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15169     this.domain = null;
15170     this.secure = false;
15171     Roo.apply(this, config);
15172     this.state = this.readCookies();
15173 };
15174
15175 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15176     // private
15177     set : function(name, value){
15178         if(typeof value == "undefined" || value === null){
15179             this.clear(name);
15180             return;
15181         }
15182         this.setCookie(name, value);
15183         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15184     },
15185
15186     // private
15187     clear : function(name){
15188         this.clearCookie(name);
15189         Roo.state.CookieProvider.superclass.clear.call(this, name);
15190     },
15191
15192     // private
15193     readCookies : function(){
15194         var cookies = {};
15195         var c = document.cookie + ";";
15196         var re = /\s?(.*?)=(.*?);/g;
15197         var matches;
15198         while((matches = re.exec(c)) != null){
15199             var name = matches[1];
15200             var value = matches[2];
15201             if(name && name.substring(0,3) == "ys-"){
15202                 cookies[name.substr(3)] = this.decodeValue(value);
15203             }
15204         }
15205         return cookies;
15206     },
15207
15208     // private
15209     setCookie : function(name, value){
15210         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15211            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15212            ((this.path == null) ? "" : ("; path=" + this.path)) +
15213            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15214            ((this.secure == true) ? "; secure" : "");
15215     },
15216
15217     // private
15218     clearCookie : function(name){
15219         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15220            ((this.path == null) ? "" : ("; path=" + this.path)) +
15221            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15222            ((this.secure == true) ? "; secure" : "");
15223     }
15224 });/*
15225  * Based on:
15226  * Ext JS Library 1.1.1
15227  * Copyright(c) 2006-2007, Ext JS, LLC.
15228  *
15229  * Originally Released Under LGPL - original licence link has changed is not relivant.
15230  *
15231  * Fork - LGPL
15232  * <script type="text/javascript">
15233  */
15234  
15235
15236 /**
15237  * @class Roo.ComponentMgr
15238  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15239  * @singleton
15240  */
15241 Roo.ComponentMgr = function(){
15242     var all = new Roo.util.MixedCollection();
15243
15244     return {
15245         /**
15246          * Registers a component.
15247          * @param {Roo.Component} c The component
15248          */
15249         register : function(c){
15250             all.add(c);
15251         },
15252
15253         /**
15254          * Unregisters a component.
15255          * @param {Roo.Component} c The component
15256          */
15257         unregister : function(c){
15258             all.remove(c);
15259         },
15260
15261         /**
15262          * Returns a component by id
15263          * @param {String} id The component id
15264          */
15265         get : function(id){
15266             return all.get(id);
15267         },
15268
15269         /**
15270          * Registers a function that will be called when a specified component is added to ComponentMgr
15271          * @param {String} id The component id
15272          * @param {Funtction} fn The callback function
15273          * @param {Object} scope The scope of the callback
15274          */
15275         onAvailable : function(id, fn, scope){
15276             all.on("add", function(index, o){
15277                 if(o.id == id){
15278                     fn.call(scope || o, o);
15279                     all.un("add", fn, scope);
15280                 }
15281             });
15282         }
15283     };
15284 }();/*
15285  * Based on:
15286  * Ext JS Library 1.1.1
15287  * Copyright(c) 2006-2007, Ext JS, LLC.
15288  *
15289  * Originally Released Under LGPL - original licence link has changed is not relivant.
15290  *
15291  * Fork - LGPL
15292  * <script type="text/javascript">
15293  */
15294  
15295 /**
15296  * @class Roo.Component
15297  * @extends Roo.util.Observable
15298  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15299  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15300  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15301  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15302  * All visual components (widgets) that require rendering into a layout should subclass Component.
15303  * @constructor
15304  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15305  * 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
15306  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15307  */
15308 Roo.Component = function(config){
15309     config = config || {};
15310     if(config.tagName || config.dom || typeof config == "string"){ // element object
15311         config = {el: config, id: config.id || config};
15312     }
15313     this.initialConfig = config;
15314
15315     Roo.apply(this, config);
15316     this.addEvents({
15317         /**
15318          * @event disable
15319          * Fires after the component is disabled.
15320              * @param {Roo.Component} this
15321              */
15322         disable : true,
15323         /**
15324          * @event enable
15325          * Fires after the component is enabled.
15326              * @param {Roo.Component} this
15327              */
15328         enable : true,
15329         /**
15330          * @event beforeshow
15331          * Fires before the component is shown.  Return false to stop the show.
15332              * @param {Roo.Component} this
15333              */
15334         beforeshow : true,
15335         /**
15336          * @event show
15337          * Fires after the component is shown.
15338              * @param {Roo.Component} this
15339              */
15340         show : true,
15341         /**
15342          * @event beforehide
15343          * Fires before the component is hidden. Return false to stop the hide.
15344              * @param {Roo.Component} this
15345              */
15346         beforehide : true,
15347         /**
15348          * @event hide
15349          * Fires after the component is hidden.
15350              * @param {Roo.Component} this
15351              */
15352         hide : true,
15353         /**
15354          * @event beforerender
15355          * Fires before the component is rendered. Return false to stop the render.
15356              * @param {Roo.Component} this
15357              */
15358         beforerender : true,
15359         /**
15360          * @event render
15361          * Fires after the component is rendered.
15362              * @param {Roo.Component} this
15363              */
15364         render : true,
15365         /**
15366          * @event beforedestroy
15367          * Fires before the component is destroyed. Return false to stop the destroy.
15368              * @param {Roo.Component} this
15369              */
15370         beforedestroy : true,
15371         /**
15372          * @event destroy
15373          * Fires after the component is destroyed.
15374              * @param {Roo.Component} this
15375              */
15376         destroy : true
15377     });
15378     if(!this.id){
15379         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15380     }
15381     Roo.ComponentMgr.register(this);
15382     Roo.Component.superclass.constructor.call(this);
15383     this.initComponent();
15384     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15385         this.render(this.renderTo);
15386         delete this.renderTo;
15387     }
15388 };
15389
15390 /** @private */
15391 Roo.Component.AUTO_ID = 1000;
15392
15393 Roo.extend(Roo.Component, Roo.util.Observable, {
15394     /**
15395      * @scope Roo.Component.prototype
15396      * @type {Boolean}
15397      * true if this component is hidden. Read-only.
15398      */
15399     hidden : false,
15400     /**
15401      * @type {Boolean}
15402      * true if this component is disabled. Read-only.
15403      */
15404     disabled : false,
15405     /**
15406      * @type {Boolean}
15407      * true if this component has been rendered. Read-only.
15408      */
15409     rendered : false,
15410     
15411     /** @cfg {String} disableClass
15412      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15413      */
15414     disabledClass : "x-item-disabled",
15415         /** @cfg {Boolean} allowDomMove
15416          * Whether the component can move the Dom node when rendering (defaults to true).
15417          */
15418     allowDomMove : true,
15419     /** @cfg {String} hideMode (display|visibility)
15420      * How this component should hidden. Supported values are
15421      * "visibility" (css visibility), "offsets" (negative offset position) and
15422      * "display" (css display) - defaults to "display".
15423      */
15424     hideMode: 'display',
15425
15426     /** @private */
15427     ctype : "Roo.Component",
15428
15429     /**
15430      * @cfg {String} actionMode 
15431      * which property holds the element that used for  hide() / show() / disable() / enable()
15432      * default is 'el' 
15433      */
15434     actionMode : "el",
15435
15436     /** @private */
15437     getActionEl : function(){
15438         return this[this.actionMode];
15439     },
15440
15441     initComponent : Roo.emptyFn,
15442     /**
15443      * If this is a lazy rendering component, render it to its container element.
15444      * @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.
15445      */
15446     render : function(container, position){
15447         
15448         if(this.rendered){
15449             return this;
15450         }
15451         
15452         if(this.fireEvent("beforerender", this) === false){
15453             return false;
15454         }
15455         
15456         if(!container && this.el){
15457             this.el = Roo.get(this.el);
15458             container = this.el.dom.parentNode;
15459             this.allowDomMove = false;
15460         }
15461         this.container = Roo.get(container);
15462         this.rendered = true;
15463         if(position !== undefined){
15464             if(typeof position == 'number'){
15465                 position = this.container.dom.childNodes[position];
15466             }else{
15467                 position = Roo.getDom(position);
15468             }
15469         }
15470         this.onRender(this.container, position || null);
15471         if(this.cls){
15472             this.el.addClass(this.cls);
15473             delete this.cls;
15474         }
15475         if(this.style){
15476             this.el.applyStyles(this.style);
15477             delete this.style;
15478         }
15479         this.fireEvent("render", this);
15480         this.afterRender(this.container);
15481         if(this.hidden){
15482             this.hide();
15483         }
15484         if(this.disabled){
15485             this.disable();
15486         }
15487
15488         return this;
15489         
15490     },
15491
15492     /** @private */
15493     // default function is not really useful
15494     onRender : function(ct, position){
15495         if(this.el){
15496             this.el = Roo.get(this.el);
15497             if(this.allowDomMove !== false){
15498                 ct.dom.insertBefore(this.el.dom, position);
15499             }
15500         }
15501     },
15502
15503     /** @private */
15504     getAutoCreate : function(){
15505         var cfg = typeof this.autoCreate == "object" ?
15506                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15507         if(this.id && !cfg.id){
15508             cfg.id = this.id;
15509         }
15510         return cfg;
15511     },
15512
15513     /** @private */
15514     afterRender : Roo.emptyFn,
15515
15516     /**
15517      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15518      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15519      */
15520     destroy : function(){
15521         if(this.fireEvent("beforedestroy", this) !== false){
15522             this.purgeListeners();
15523             this.beforeDestroy();
15524             if(this.rendered){
15525                 this.el.removeAllListeners();
15526                 this.el.remove();
15527                 if(this.actionMode == "container"){
15528                     this.container.remove();
15529                 }
15530             }
15531             this.onDestroy();
15532             Roo.ComponentMgr.unregister(this);
15533             this.fireEvent("destroy", this);
15534         }
15535     },
15536
15537         /** @private */
15538     beforeDestroy : function(){
15539
15540     },
15541
15542         /** @private */
15543         onDestroy : function(){
15544
15545     },
15546
15547     /**
15548      * Returns the underlying {@link Roo.Element}.
15549      * @return {Roo.Element} The element
15550      */
15551     getEl : function(){
15552         return this.el;
15553     },
15554
15555     /**
15556      * Returns the id of this component.
15557      * @return {String}
15558      */
15559     getId : function(){
15560         return this.id;
15561     },
15562
15563     /**
15564      * Try to focus this component.
15565      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15566      * @return {Roo.Component} this
15567      */
15568     focus : function(selectText){
15569         if(this.rendered){
15570             this.el.focus();
15571             if(selectText === true){
15572                 this.el.dom.select();
15573             }
15574         }
15575         return this;
15576     },
15577
15578     /** @private */
15579     blur : function(){
15580         if(this.rendered){
15581             this.el.blur();
15582         }
15583         return this;
15584     },
15585
15586     /**
15587      * Disable this component.
15588      * @return {Roo.Component} this
15589      */
15590     disable : function(){
15591         if(this.rendered){
15592             this.onDisable();
15593         }
15594         this.disabled = true;
15595         this.fireEvent("disable", this);
15596         return this;
15597     },
15598
15599         // private
15600     onDisable : function(){
15601         this.getActionEl().addClass(this.disabledClass);
15602         this.el.dom.disabled = true;
15603     },
15604
15605     /**
15606      * Enable this component.
15607      * @return {Roo.Component} this
15608      */
15609     enable : function(){
15610         if(this.rendered){
15611             this.onEnable();
15612         }
15613         this.disabled = false;
15614         this.fireEvent("enable", this);
15615         return this;
15616     },
15617
15618         // private
15619     onEnable : function(){
15620         this.getActionEl().removeClass(this.disabledClass);
15621         this.el.dom.disabled = false;
15622     },
15623
15624     /**
15625      * Convenience function for setting disabled/enabled by boolean.
15626      * @param {Boolean} disabled
15627      */
15628     setDisabled : function(disabled){
15629         this[disabled ? "disable" : "enable"]();
15630     },
15631
15632     /**
15633      * Show this component.
15634      * @return {Roo.Component} this
15635      */
15636     show: function(){
15637         if(this.fireEvent("beforeshow", this) !== false){
15638             this.hidden = false;
15639             if(this.rendered){
15640                 this.onShow();
15641             }
15642             this.fireEvent("show", this);
15643         }
15644         return this;
15645     },
15646
15647     // private
15648     onShow : function(){
15649         var ae = this.getActionEl();
15650         if(this.hideMode == 'visibility'){
15651             ae.dom.style.visibility = "visible";
15652         }else if(this.hideMode == 'offsets'){
15653             ae.removeClass('x-hidden');
15654         }else{
15655             ae.dom.style.display = "";
15656         }
15657     },
15658
15659     /**
15660      * Hide this component.
15661      * @return {Roo.Component} this
15662      */
15663     hide: function(){
15664         if(this.fireEvent("beforehide", this) !== false){
15665             this.hidden = true;
15666             if(this.rendered){
15667                 this.onHide();
15668             }
15669             this.fireEvent("hide", this);
15670         }
15671         return this;
15672     },
15673
15674     // private
15675     onHide : function(){
15676         var ae = this.getActionEl();
15677         if(this.hideMode == 'visibility'){
15678             ae.dom.style.visibility = "hidden";
15679         }else if(this.hideMode == 'offsets'){
15680             ae.addClass('x-hidden');
15681         }else{
15682             ae.dom.style.display = "none";
15683         }
15684     },
15685
15686     /**
15687      * Convenience function to hide or show this component by boolean.
15688      * @param {Boolean} visible True to show, false to hide
15689      * @return {Roo.Component} this
15690      */
15691     setVisible: function(visible){
15692         if(visible) {
15693             this.show();
15694         }else{
15695             this.hide();
15696         }
15697         return this;
15698     },
15699
15700     /**
15701      * Returns true if this component is visible.
15702      */
15703     isVisible : function(){
15704         return this.getActionEl().isVisible();
15705     },
15706
15707     cloneConfig : function(overrides){
15708         overrides = overrides || {};
15709         var id = overrides.id || Roo.id();
15710         var cfg = Roo.applyIf(overrides, this.initialConfig);
15711         cfg.id = id; // prevent dup id
15712         return new this.constructor(cfg);
15713     }
15714 });/*
15715  * Based on:
15716  * Ext JS Library 1.1.1
15717  * Copyright(c) 2006-2007, Ext JS, LLC.
15718  *
15719  * Originally Released Under LGPL - original licence link has changed is not relivant.
15720  *
15721  * Fork - LGPL
15722  * <script type="text/javascript">
15723  */
15724
15725 /**
15726  * @class Roo.BoxComponent
15727  * @extends Roo.Component
15728  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15729  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15730  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15731  * layout containers.
15732  * @constructor
15733  * @param {Roo.Element/String/Object} config The configuration options.
15734  */
15735 Roo.BoxComponent = function(config){
15736     Roo.Component.call(this, config);
15737     this.addEvents({
15738         /**
15739          * @event resize
15740          * Fires after the component is resized.
15741              * @param {Roo.Component} this
15742              * @param {Number} adjWidth The box-adjusted width that was set
15743              * @param {Number} adjHeight The box-adjusted height that was set
15744              * @param {Number} rawWidth The width that was originally specified
15745              * @param {Number} rawHeight The height that was originally specified
15746              */
15747         resize : true,
15748         /**
15749          * @event move
15750          * Fires after the component is moved.
15751              * @param {Roo.Component} this
15752              * @param {Number} x The new x position
15753              * @param {Number} y The new y position
15754              */
15755         move : true
15756     });
15757 };
15758
15759 Roo.extend(Roo.BoxComponent, Roo.Component, {
15760     // private, set in afterRender to signify that the component has been rendered
15761     boxReady : false,
15762     // private, used to defer height settings to subclasses
15763     deferHeight: false,
15764     /** @cfg {Number} width
15765      * width (optional) size of component
15766      */
15767      /** @cfg {Number} height
15768      * height (optional) size of component
15769      */
15770      
15771     /**
15772      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15773      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15774      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15775      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15776      * @return {Roo.BoxComponent} this
15777      */
15778     setSize : function(w, h){
15779         // support for standard size objects
15780         if(typeof w == 'object'){
15781             h = w.height;
15782             w = w.width;
15783         }
15784         // not rendered
15785         if(!this.boxReady){
15786             this.width = w;
15787             this.height = h;
15788             return this;
15789         }
15790
15791         // prevent recalcs when not needed
15792         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15793             return this;
15794         }
15795         this.lastSize = {width: w, height: h};
15796
15797         var adj = this.adjustSize(w, h);
15798         var aw = adj.width, ah = adj.height;
15799         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15800             var rz = this.getResizeEl();
15801             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15802                 rz.setSize(aw, ah);
15803             }else if(!this.deferHeight && ah !== undefined){
15804                 rz.setHeight(ah);
15805             }else if(aw !== undefined){
15806                 rz.setWidth(aw);
15807             }
15808             this.onResize(aw, ah, w, h);
15809             this.fireEvent('resize', this, aw, ah, w, h);
15810         }
15811         return this;
15812     },
15813
15814     /**
15815      * Gets the current size of the component's underlying element.
15816      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15817      */
15818     getSize : function(){
15819         return this.el.getSize();
15820     },
15821
15822     /**
15823      * Gets the current XY position of the component's underlying element.
15824      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15825      * @return {Array} The XY position of the element (e.g., [100, 200])
15826      */
15827     getPosition : function(local){
15828         if(local === true){
15829             return [this.el.getLeft(true), this.el.getTop(true)];
15830         }
15831         return this.xy || this.el.getXY();
15832     },
15833
15834     /**
15835      * Gets the current box measurements of the component's underlying element.
15836      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15837      * @returns {Object} box An object in the format {x, y, width, height}
15838      */
15839     getBox : function(local){
15840         var s = this.el.getSize();
15841         if(local){
15842             s.x = this.el.getLeft(true);
15843             s.y = this.el.getTop(true);
15844         }else{
15845             var xy = this.xy || this.el.getXY();
15846             s.x = xy[0];
15847             s.y = xy[1];
15848         }
15849         return s;
15850     },
15851
15852     /**
15853      * Sets the current box measurements of the component's underlying element.
15854      * @param {Object} box An object in the format {x, y, width, height}
15855      * @returns {Roo.BoxComponent} this
15856      */
15857     updateBox : function(box){
15858         this.setSize(box.width, box.height);
15859         this.setPagePosition(box.x, box.y);
15860         return this;
15861     },
15862
15863     // protected
15864     getResizeEl : function(){
15865         return this.resizeEl || this.el;
15866     },
15867
15868     // protected
15869     getPositionEl : function(){
15870         return this.positionEl || this.el;
15871     },
15872
15873     /**
15874      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15875      * This method fires the move event.
15876      * @param {Number} left The new left
15877      * @param {Number} top The new top
15878      * @returns {Roo.BoxComponent} this
15879      */
15880     setPosition : function(x, y){
15881         this.x = x;
15882         this.y = y;
15883         if(!this.boxReady){
15884             return this;
15885         }
15886         var adj = this.adjustPosition(x, y);
15887         var ax = adj.x, ay = adj.y;
15888
15889         var el = this.getPositionEl();
15890         if(ax !== undefined || ay !== undefined){
15891             if(ax !== undefined && ay !== undefined){
15892                 el.setLeftTop(ax, ay);
15893             }else if(ax !== undefined){
15894                 el.setLeft(ax);
15895             }else if(ay !== undefined){
15896                 el.setTop(ay);
15897             }
15898             this.onPosition(ax, ay);
15899             this.fireEvent('move', this, ax, ay);
15900         }
15901         return this;
15902     },
15903
15904     /**
15905      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15906      * This method fires the move event.
15907      * @param {Number} x The new x position
15908      * @param {Number} y The new y position
15909      * @returns {Roo.BoxComponent} this
15910      */
15911     setPagePosition : function(x, y){
15912         this.pageX = x;
15913         this.pageY = y;
15914         if(!this.boxReady){
15915             return;
15916         }
15917         if(x === undefined || y === undefined){ // cannot translate undefined points
15918             return;
15919         }
15920         var p = this.el.translatePoints(x, y);
15921         this.setPosition(p.left, p.top);
15922         return this;
15923     },
15924
15925     // private
15926     onRender : function(ct, position){
15927         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15928         if(this.resizeEl){
15929             this.resizeEl = Roo.get(this.resizeEl);
15930         }
15931         if(this.positionEl){
15932             this.positionEl = Roo.get(this.positionEl);
15933         }
15934     },
15935
15936     // private
15937     afterRender : function(){
15938         Roo.BoxComponent.superclass.afterRender.call(this);
15939         this.boxReady = true;
15940         this.setSize(this.width, this.height);
15941         if(this.x || this.y){
15942             this.setPosition(this.x, this.y);
15943         }
15944         if(this.pageX || this.pageY){
15945             this.setPagePosition(this.pageX, this.pageY);
15946         }
15947     },
15948
15949     /**
15950      * Force the component's size to recalculate based on the underlying element's current height and width.
15951      * @returns {Roo.BoxComponent} this
15952      */
15953     syncSize : function(){
15954         delete this.lastSize;
15955         this.setSize(this.el.getWidth(), this.el.getHeight());
15956         return this;
15957     },
15958
15959     /**
15960      * Called after the component is resized, this method is empty by default but can be implemented by any
15961      * subclass that needs to perform custom logic after a resize occurs.
15962      * @param {Number} adjWidth The box-adjusted width that was set
15963      * @param {Number} adjHeight The box-adjusted height that was set
15964      * @param {Number} rawWidth The width that was originally specified
15965      * @param {Number} rawHeight The height that was originally specified
15966      */
15967     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15968
15969     },
15970
15971     /**
15972      * Called after the component is moved, this method is empty by default but can be implemented by any
15973      * subclass that needs to perform custom logic after a move occurs.
15974      * @param {Number} x The new x position
15975      * @param {Number} y The new y position
15976      */
15977     onPosition : function(x, y){
15978
15979     },
15980
15981     // private
15982     adjustSize : function(w, h){
15983         if(this.autoWidth){
15984             w = 'auto';
15985         }
15986         if(this.autoHeight){
15987             h = 'auto';
15988         }
15989         return {width : w, height: h};
15990     },
15991
15992     // private
15993     adjustPosition : function(x, y){
15994         return {x : x, y: y};
15995     }
15996 });/*
15997  * Original code for Roojs - LGPL
15998  * <script type="text/javascript">
15999  */
16000  
16001 /**
16002  * @class Roo.XComponent
16003  * A delayed Element creator...
16004  * Or a way to group chunks of interface together.
16005  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16006  *  used in conjunction with XComponent.build() it will create an instance of each element,
16007  *  then call addxtype() to build the User interface.
16008  * 
16009  * Mypart.xyx = new Roo.XComponent({
16010
16011     parent : 'Mypart.xyz', // empty == document.element.!!
16012     order : '001',
16013     name : 'xxxx'
16014     region : 'xxxx'
16015     disabled : function() {} 
16016      
16017     tree : function() { // return an tree of xtype declared components
16018         var MODULE = this;
16019         return 
16020         {
16021             xtype : 'NestedLayoutPanel',
16022             // technicall
16023         }
16024      ]
16025  *})
16026  *
16027  *
16028  * It can be used to build a big heiracy, with parent etc.
16029  * or you can just use this to render a single compoent to a dom element
16030  * MYPART.render(Roo.Element | String(id) | dom_element )
16031  *
16032  *
16033  * Usage patterns.
16034  *
16035  * Classic Roo
16036  *
16037  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16038  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16039  *
16040  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16041  *
16042  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16043  * - if mulitple topModules exist, the last one is defined as the top module.
16044  *
16045  * Embeded Roo
16046  * 
16047  * When the top level or multiple modules are to embedded into a existing HTML page,
16048  * the parent element can container '#id' of the element where the module will be drawn.
16049  *
16050  * Bootstrap Roo
16051  *
16052  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16053  * it relies more on a include mechanism, where sub modules are included into an outer page.
16054  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16055  * 
16056  * Bootstrap Roo Included elements
16057  *
16058  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16059  * hence confusing the component builder as it thinks there are multiple top level elements. 
16060  *
16061  * String Over-ride & Translations
16062  *
16063  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16064  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16065  * are needed. @see Roo.XComponent.overlayString  
16066  * 
16067  * 
16068  * 
16069  * @extends Roo.util.Observable
16070  * @constructor
16071  * @param cfg {Object} configuration of component
16072  * 
16073  */
16074 Roo.XComponent = function(cfg) {
16075     Roo.apply(this, cfg);
16076     this.addEvents({ 
16077         /**
16078              * @event built
16079              * Fires when this the componnt is built
16080              * @param {Roo.XComponent} c the component
16081              */
16082         'built' : true
16083         
16084     });
16085     this.region = this.region || 'center'; // default..
16086     Roo.XComponent.register(this);
16087     this.modules = false;
16088     this.el = false; // where the layout goes..
16089     
16090     
16091 }
16092 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16093     /**
16094      * @property el
16095      * The created element (with Roo.factory())
16096      * @type {Roo.Layout}
16097      */
16098     el  : false,
16099     
16100     /**
16101      * @property el
16102      * for BC  - use el in new code
16103      * @type {Roo.Layout}
16104      */
16105     panel : false,
16106     
16107     /**
16108      * @property layout
16109      * for BC  - use el in new code
16110      * @type {Roo.Layout}
16111      */
16112     layout : false,
16113     
16114      /**
16115      * @cfg {Function|boolean} disabled
16116      * If this module is disabled by some rule, return true from the funtion
16117      */
16118     disabled : false,
16119     
16120     /**
16121      * @cfg {String} parent 
16122      * Name of parent element which it get xtype added to..
16123      */
16124     parent: false,
16125     
16126     /**
16127      * @cfg {String} order
16128      * Used to set the order in which elements are created (usefull for multiple tabs)
16129      */
16130     
16131     order : false,
16132     /**
16133      * @cfg {String} name
16134      * String to display while loading.
16135      */
16136     name : false,
16137     /**
16138      * @cfg {String} region
16139      * Region to render component to (defaults to center)
16140      */
16141     region : 'center',
16142     
16143     /**
16144      * @cfg {Array} items
16145      * A single item array - the first element is the root of the tree..
16146      * It's done this way to stay compatible with the Xtype system...
16147      */
16148     items : false,
16149     
16150     /**
16151      * @property _tree
16152      * The method that retuns the tree of parts that make up this compoennt 
16153      * @type {function}
16154      */
16155     _tree  : false,
16156     
16157      /**
16158      * render
16159      * render element to dom or tree
16160      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16161      */
16162     
16163     render : function(el)
16164     {
16165         
16166         el = el || false;
16167         var hp = this.parent ? 1 : 0;
16168         Roo.debug &&  Roo.log(this);
16169         
16170         var tree = this._tree ? this._tree() : this.tree();
16171
16172         
16173         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16174             // if parent is a '#.....' string, then let's use that..
16175             var ename = this.parent.substr(1);
16176             this.parent = false;
16177             Roo.debug && Roo.log(ename);
16178             switch (ename) {
16179                 case 'bootstrap-body':
16180                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16181                         // this is the BorderLayout standard?
16182                        this.parent = { el : true };
16183                        break;
16184                     }
16185                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16186                         // need to insert stuff...
16187                         this.parent =  {
16188                              el : new Roo.bootstrap.layout.Border({
16189                                  el : document.body, 
16190                      
16191                                  center: {
16192                                     titlebar: false,
16193                                     autoScroll:false,
16194                                     closeOnTab: true,
16195                                     tabPosition: 'top',
16196                                       //resizeTabs: true,
16197                                     alwaysShowTabs: true,
16198                                     hideTabs: false
16199                                      //minTabWidth: 140
16200                                  }
16201                              })
16202                         
16203                          };
16204                          break;
16205                     }
16206                          
16207                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16208                         this.parent = { el :  new  Roo.bootstrap.Body() };
16209                         Roo.debug && Roo.log("setting el to doc body");
16210                          
16211                     } else {
16212                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16213                     }
16214                     break;
16215                 case 'bootstrap':
16216                     this.parent = { el : true};
16217                     // fall through
16218                 default:
16219                     el = Roo.get(ename);
16220                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16221                         this.parent = { el : true};
16222                     }
16223                     
16224                     break;
16225             }
16226                 
16227             
16228             if (!el && !this.parent) {
16229                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16230                 return;
16231             }
16232         }
16233         
16234         Roo.debug && Roo.log("EL:");
16235         Roo.debug && Roo.log(el);
16236         Roo.debug && Roo.log("this.parent.el:");
16237         Roo.debug && Roo.log(this.parent.el);
16238         
16239
16240         // altertive root elements ??? - we need a better way to indicate these.
16241         var is_alt = Roo.XComponent.is_alt ||
16242                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16243                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16244                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16245         
16246         
16247         
16248         if (!this.parent && is_alt) {
16249             //el = Roo.get(document.body);
16250             this.parent = { el : true };
16251         }
16252             
16253             
16254         
16255         if (!this.parent) {
16256             
16257             Roo.debug && Roo.log("no parent - creating one");
16258             
16259             el = el ? Roo.get(el) : false;      
16260             
16261             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16262                 
16263                 this.parent =  {
16264                     el : new Roo.bootstrap.layout.Border({
16265                         el: el || document.body,
16266                     
16267                         center: {
16268                             titlebar: false,
16269                             autoScroll:false,
16270                             closeOnTab: true,
16271                             tabPosition: 'top',
16272                              //resizeTabs: true,
16273                             alwaysShowTabs: false,
16274                             hideTabs: true,
16275                             minTabWidth: 140,
16276                             overflow: 'visible'
16277                          }
16278                      })
16279                 };
16280             } else {
16281             
16282                 // it's a top level one..
16283                 this.parent =  {
16284                     el : new Roo.BorderLayout(el || document.body, {
16285                         center: {
16286                             titlebar: false,
16287                             autoScroll:false,
16288                             closeOnTab: true,
16289                             tabPosition: 'top',
16290                              //resizeTabs: true,
16291                             alwaysShowTabs: el && hp? false :  true,
16292                             hideTabs: el || !hp ? true :  false,
16293                             minTabWidth: 140
16294                          }
16295                     })
16296                 };
16297             }
16298         }
16299         
16300         if (!this.parent.el) {
16301                 // probably an old style ctor, which has been disabled.
16302                 return;
16303
16304         }
16305                 // The 'tree' method is  '_tree now' 
16306             
16307         tree.region = tree.region || this.region;
16308         var is_body = false;
16309         if (this.parent.el === true) {
16310             // bootstrap... - body..
16311             if (el) {
16312                 tree.el = el;
16313             }
16314             this.parent.el = Roo.factory(tree);
16315             is_body = true;
16316         }
16317         
16318         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16319         this.fireEvent('built', this);
16320         
16321         this.panel = this.el;
16322         this.layout = this.panel.layout;
16323         this.parentLayout = this.parent.layout  || false;  
16324          
16325     }
16326     
16327 });
16328
16329 Roo.apply(Roo.XComponent, {
16330     /**
16331      * @property  hideProgress
16332      * true to disable the building progress bar.. usefull on single page renders.
16333      * @type Boolean
16334      */
16335     hideProgress : false,
16336     /**
16337      * @property  buildCompleted
16338      * True when the builder has completed building the interface.
16339      * @type Boolean
16340      */
16341     buildCompleted : false,
16342      
16343     /**
16344      * @property  topModule
16345      * the upper most module - uses document.element as it's constructor.
16346      * @type Object
16347      */
16348      
16349     topModule  : false,
16350       
16351     /**
16352      * @property  modules
16353      * array of modules to be created by registration system.
16354      * @type {Array} of Roo.XComponent
16355      */
16356     
16357     modules : [],
16358     /**
16359      * @property  elmodules
16360      * array of modules to be created by which use #ID 
16361      * @type {Array} of Roo.XComponent
16362      */
16363      
16364     elmodules : [],
16365
16366      /**
16367      * @property  is_alt
16368      * Is an alternative Root - normally used by bootstrap or other systems,
16369      *    where the top element in the tree can wrap 'body' 
16370      * @type {boolean}  (default false)
16371      */
16372      
16373     is_alt : false,
16374     /**
16375      * @property  build_from_html
16376      * Build elements from html - used by bootstrap HTML stuff 
16377      *    - this is cleared after build is completed
16378      * @type {boolean}    (default false)
16379      */
16380      
16381     build_from_html : false,
16382     /**
16383      * Register components to be built later.
16384      *
16385      * This solves the following issues
16386      * - Building is not done on page load, but after an authentication process has occured.
16387      * - Interface elements are registered on page load
16388      * - Parent Interface elements may not be loaded before child, so this handles that..
16389      * 
16390      *
16391      * example:
16392      * 
16393      * MyApp.register({
16394           order : '000001',
16395           module : 'Pman.Tab.projectMgr',
16396           region : 'center',
16397           parent : 'Pman.layout',
16398           disabled : false,  // or use a function..
16399         })
16400      
16401      * * @param {Object} details about module
16402      */
16403     register : function(obj) {
16404                 
16405         Roo.XComponent.event.fireEvent('register', obj);
16406         switch(typeof(obj.disabled) ) {
16407                 
16408             case 'undefined':
16409                 break;
16410             
16411             case 'function':
16412                 if ( obj.disabled() ) {
16413                         return;
16414                 }
16415                 break;
16416             
16417             default:
16418                 if (obj.disabled) {
16419                         return;
16420                 }
16421                 break;
16422         }
16423                 
16424         this.modules.push(obj);
16425          
16426     },
16427     /**
16428      * convert a string to an object..
16429      * eg. 'AAA.BBB' -> finds AAA.BBB
16430
16431      */
16432     
16433     toObject : function(str)
16434     {
16435         if (!str || typeof(str) == 'object') {
16436             return str;
16437         }
16438         if (str.substring(0,1) == '#') {
16439             return str;
16440         }
16441
16442         var ar = str.split('.');
16443         var rt, o;
16444         rt = ar.shift();
16445             /** eval:var:o */
16446         try {
16447             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16448         } catch (e) {
16449             throw "Module not found : " + str;
16450         }
16451         
16452         if (o === false) {
16453             throw "Module not found : " + str;
16454         }
16455         Roo.each(ar, function(e) {
16456             if (typeof(o[e]) == 'undefined') {
16457                 throw "Module not found : " + str;
16458             }
16459             o = o[e];
16460         });
16461         
16462         return o;
16463         
16464     },
16465     
16466     
16467     /**
16468      * move modules into their correct place in the tree..
16469      * 
16470      */
16471     preBuild : function ()
16472     {
16473         var _t = this;
16474         Roo.each(this.modules , function (obj)
16475         {
16476             Roo.XComponent.event.fireEvent('beforebuild', obj);
16477             
16478             var opar = obj.parent;
16479             try { 
16480                 obj.parent = this.toObject(opar);
16481             } catch(e) {
16482                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16483                 return;
16484             }
16485             
16486             if (!obj.parent) {
16487                 Roo.debug && Roo.log("GOT top level module");
16488                 Roo.debug && Roo.log(obj);
16489                 obj.modules = new Roo.util.MixedCollection(false, 
16490                     function(o) { return o.order + '' }
16491                 );
16492                 this.topModule = obj;
16493                 return;
16494             }
16495                         // parent is a string (usually a dom element name..)
16496             if (typeof(obj.parent) == 'string') {
16497                 this.elmodules.push(obj);
16498                 return;
16499             }
16500             if (obj.parent.constructor != Roo.XComponent) {
16501                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16502             }
16503             if (!obj.parent.modules) {
16504                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16505                     function(o) { return o.order + '' }
16506                 );
16507             }
16508             if (obj.parent.disabled) {
16509                 obj.disabled = true;
16510             }
16511             obj.parent.modules.add(obj);
16512         }, this);
16513     },
16514     
16515      /**
16516      * make a list of modules to build.
16517      * @return {Array} list of modules. 
16518      */ 
16519     
16520     buildOrder : function()
16521     {
16522         var _this = this;
16523         var cmp = function(a,b) {   
16524             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16525         };
16526         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16527             throw "No top level modules to build";
16528         }
16529         
16530         // make a flat list in order of modules to build.
16531         var mods = this.topModule ? [ this.topModule ] : [];
16532                 
16533         
16534         // elmodules (is a list of DOM based modules )
16535         Roo.each(this.elmodules, function(e) {
16536             mods.push(e);
16537             if (!this.topModule &&
16538                 typeof(e.parent) == 'string' &&
16539                 e.parent.substring(0,1) == '#' &&
16540                 Roo.get(e.parent.substr(1))
16541                ) {
16542                 
16543                 _this.topModule = e;
16544             }
16545             
16546         });
16547
16548         
16549         // add modules to their parents..
16550         var addMod = function(m) {
16551             Roo.debug && Roo.log("build Order: add: " + m.name);
16552                 
16553             mods.push(m);
16554             if (m.modules && !m.disabled) {
16555                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16556                 m.modules.keySort('ASC',  cmp );
16557                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16558     
16559                 m.modules.each(addMod);
16560             } else {
16561                 Roo.debug && Roo.log("build Order: no child modules");
16562             }
16563             // not sure if this is used any more..
16564             if (m.finalize) {
16565                 m.finalize.name = m.name + " (clean up) ";
16566                 mods.push(m.finalize);
16567             }
16568             
16569         }
16570         if (this.topModule && this.topModule.modules) { 
16571             this.topModule.modules.keySort('ASC',  cmp );
16572             this.topModule.modules.each(addMod);
16573         } 
16574         return mods;
16575     },
16576     
16577      /**
16578      * Build the registered modules.
16579      * @param {Object} parent element.
16580      * @param {Function} optional method to call after module has been added.
16581      * 
16582      */ 
16583    
16584     build : function(opts) 
16585     {
16586         
16587         if (typeof(opts) != 'undefined') {
16588             Roo.apply(this,opts);
16589         }
16590         
16591         this.preBuild();
16592         var mods = this.buildOrder();
16593       
16594         //this.allmods = mods;
16595         //Roo.debug && Roo.log(mods);
16596         //return;
16597         if (!mods.length) { // should not happen
16598             throw "NO modules!!!";
16599         }
16600         
16601         
16602         var msg = "Building Interface...";
16603         // flash it up as modal - so we store the mask!?
16604         if (!this.hideProgress && Roo.MessageBox) {
16605             Roo.MessageBox.show({ title: 'loading' });
16606             Roo.MessageBox.show({
16607                title: "Please wait...",
16608                msg: msg,
16609                width:450,
16610                progress:true,
16611                buttons : false,
16612                closable:false,
16613                modal: false
16614               
16615             });
16616         }
16617         var total = mods.length;
16618         
16619         var _this = this;
16620         var progressRun = function() {
16621             if (!mods.length) {
16622                 Roo.debug && Roo.log('hide?');
16623                 if (!this.hideProgress && Roo.MessageBox) {
16624                     Roo.MessageBox.hide();
16625                 }
16626                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16627                 
16628                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16629                 
16630                 // THE END...
16631                 return false;   
16632             }
16633             
16634             var m = mods.shift();
16635             
16636             
16637             Roo.debug && Roo.log(m);
16638             // not sure if this is supported any more.. - modules that are are just function
16639             if (typeof(m) == 'function') { 
16640                 m.call(this);
16641                 return progressRun.defer(10, _this);
16642             } 
16643             
16644             
16645             msg = "Building Interface " + (total  - mods.length) + 
16646                     " of " + total + 
16647                     (m.name ? (' - ' + m.name) : '');
16648                         Roo.debug && Roo.log(msg);
16649             if (!_this.hideProgress &&  Roo.MessageBox) { 
16650                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16651             }
16652             
16653          
16654             // is the module disabled?
16655             var disabled = (typeof(m.disabled) == 'function') ?
16656                 m.disabled.call(m.module.disabled) : m.disabled;    
16657             
16658             
16659             if (disabled) {
16660                 return progressRun(); // we do not update the display!
16661             }
16662             
16663             // now build 
16664             
16665                         
16666                         
16667             m.render();
16668             // it's 10 on top level, and 1 on others??? why...
16669             return progressRun.defer(10, _this);
16670              
16671         }
16672         progressRun.defer(1, _this);
16673      
16674         
16675         
16676     },
16677     /**
16678      * Overlay a set of modified strings onto a component
16679      * This is dependant on our builder exporting the strings and 'named strings' elements.
16680      * 
16681      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16682      * @param {Object} associative array of 'named' string and it's new value.
16683      * 
16684      */
16685         overlayStrings : function( component, strings )
16686     {
16687         if (typeof(component['_named_strings']) == 'undefined') {
16688             throw "ERROR: component does not have _named_strings";
16689         }
16690         for ( var k in strings ) {
16691             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16692             if (md !== false) {
16693                 component['_strings'][md] = strings[k];
16694             } else {
16695                 Roo.log('could not find named string: ' + k + ' in');
16696                 Roo.log(component);
16697             }
16698             
16699         }
16700         
16701     },
16702     
16703         
16704         /**
16705          * Event Object.
16706          *
16707          *
16708          */
16709         event: false, 
16710     /**
16711          * wrapper for event.on - aliased later..  
16712          * Typically use to register a event handler for register:
16713          *
16714          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16715          *
16716          */
16717     on : false
16718    
16719     
16720     
16721 });
16722
16723 Roo.XComponent.event = new Roo.util.Observable({
16724                 events : { 
16725                         /**
16726                          * @event register
16727                          * Fires when an Component is registered,
16728                          * set the disable property on the Component to stop registration.
16729                          * @param {Roo.XComponent} c the component being registerd.
16730                          * 
16731                          */
16732                         'register' : true,
16733             /**
16734                          * @event beforebuild
16735                          * Fires before each Component is built
16736                          * can be used to apply permissions.
16737                          * @param {Roo.XComponent} c the component being registerd.
16738                          * 
16739                          */
16740                         'beforebuild' : true,
16741                         /**
16742                          * @event buildcomplete
16743                          * Fires on the top level element when all elements have been built
16744                          * @param {Roo.XComponent} the top level component.
16745                          */
16746                         'buildcomplete' : true
16747                         
16748                 }
16749 });
16750
16751 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16752  //
16753  /**
16754  * marked - a markdown parser
16755  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16756  * https://github.com/chjj/marked
16757  */
16758
16759
16760 /**
16761  *
16762  * Roo.Markdown - is a very crude wrapper around marked..
16763  *
16764  * usage:
16765  * 
16766  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16767  * 
16768  * Note: move the sample code to the bottom of this
16769  * file before uncommenting it.
16770  *
16771  */
16772
16773 Roo.Markdown = {};
16774 Roo.Markdown.toHtml = function(text) {
16775     
16776     var c = new Roo.Markdown.marked.setOptions({
16777             renderer: new Roo.Markdown.marked.Renderer(),
16778             gfm: true,
16779             tables: true,
16780             breaks: false,
16781             pedantic: false,
16782             sanitize: false,
16783             smartLists: true,
16784             smartypants: false
16785           });
16786     // A FEW HACKS!!?
16787     
16788     text = text.replace(/\\\n/g,' ');
16789     return Roo.Markdown.marked(text);
16790 };
16791 //
16792 // converter
16793 //
16794 // Wraps all "globals" so that the only thing
16795 // exposed is makeHtml().
16796 //
16797 (function() {
16798     
16799     /**
16800      * Block-Level Grammar
16801      */
16802     
16803     var block = {
16804       newline: /^\n+/,
16805       code: /^( {4}[^\n]+\n*)+/,
16806       fences: noop,
16807       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16808       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16809       nptable: noop,
16810       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16811       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16812       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16813       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16814       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16815       table: noop,
16816       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16817       text: /^[^\n]+/
16818     };
16819     
16820     block.bullet = /(?:[*+-]|\d+\.)/;
16821     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16822     block.item = replace(block.item, 'gm')
16823       (/bull/g, block.bullet)
16824       ();
16825     
16826     block.list = replace(block.list)
16827       (/bull/g, block.bullet)
16828       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16829       ('def', '\\n+(?=' + block.def.source + ')')
16830       ();
16831     
16832     block.blockquote = replace(block.blockquote)
16833       ('def', block.def)
16834       ();
16835     
16836     block._tag = '(?!(?:'
16837       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16838       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16839       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16840     
16841     block.html = replace(block.html)
16842       ('comment', /<!--[\s\S]*?-->/)
16843       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16844       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16845       (/tag/g, block._tag)
16846       ();
16847     
16848     block.paragraph = replace(block.paragraph)
16849       ('hr', block.hr)
16850       ('heading', block.heading)
16851       ('lheading', block.lheading)
16852       ('blockquote', block.blockquote)
16853       ('tag', '<' + block._tag)
16854       ('def', block.def)
16855       ();
16856     
16857     /**
16858      * Normal Block Grammar
16859      */
16860     
16861     block.normal = merge({}, block);
16862     
16863     /**
16864      * GFM Block Grammar
16865      */
16866     
16867     block.gfm = merge({}, block.normal, {
16868       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16869       paragraph: /^/,
16870       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16871     });
16872     
16873     block.gfm.paragraph = replace(block.paragraph)
16874       ('(?!', '(?!'
16875         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16876         + block.list.source.replace('\\1', '\\3') + '|')
16877       ();
16878     
16879     /**
16880      * GFM + Tables Block Grammar
16881      */
16882     
16883     block.tables = merge({}, block.gfm, {
16884       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16885       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16886     });
16887     
16888     /**
16889      * Block Lexer
16890      */
16891     
16892     function Lexer(options) {
16893       this.tokens = [];
16894       this.tokens.links = {};
16895       this.options = options || marked.defaults;
16896       this.rules = block.normal;
16897     
16898       if (this.options.gfm) {
16899         if (this.options.tables) {
16900           this.rules = block.tables;
16901         } else {
16902           this.rules = block.gfm;
16903         }
16904       }
16905     }
16906     
16907     /**
16908      * Expose Block Rules
16909      */
16910     
16911     Lexer.rules = block;
16912     
16913     /**
16914      * Static Lex Method
16915      */
16916     
16917     Lexer.lex = function(src, options) {
16918       var lexer = new Lexer(options);
16919       return lexer.lex(src);
16920     };
16921     
16922     /**
16923      * Preprocessing
16924      */
16925     
16926     Lexer.prototype.lex = function(src) {
16927       src = src
16928         .replace(/\r\n|\r/g, '\n')
16929         .replace(/\t/g, '    ')
16930         .replace(/\u00a0/g, ' ')
16931         .replace(/\u2424/g, '\n');
16932     
16933       return this.token(src, true);
16934     };
16935     
16936     /**
16937      * Lexing
16938      */
16939     
16940     Lexer.prototype.token = function(src, top, bq) {
16941       var src = src.replace(/^ +$/gm, '')
16942         , next
16943         , loose
16944         , cap
16945         , bull
16946         , b
16947         , item
16948         , space
16949         , i
16950         , l;
16951     
16952       while (src) {
16953         // newline
16954         if (cap = this.rules.newline.exec(src)) {
16955           src = src.substring(cap[0].length);
16956           if (cap[0].length > 1) {
16957             this.tokens.push({
16958               type: 'space'
16959             });
16960           }
16961         }
16962     
16963         // code
16964         if (cap = this.rules.code.exec(src)) {
16965           src = src.substring(cap[0].length);
16966           cap = cap[0].replace(/^ {4}/gm, '');
16967           this.tokens.push({
16968             type: 'code',
16969             text: !this.options.pedantic
16970               ? cap.replace(/\n+$/, '')
16971               : cap
16972           });
16973           continue;
16974         }
16975     
16976         // fences (gfm)
16977         if (cap = this.rules.fences.exec(src)) {
16978           src = src.substring(cap[0].length);
16979           this.tokens.push({
16980             type: 'code',
16981             lang: cap[2],
16982             text: cap[3] || ''
16983           });
16984           continue;
16985         }
16986     
16987         // heading
16988         if (cap = this.rules.heading.exec(src)) {
16989           src = src.substring(cap[0].length);
16990           this.tokens.push({
16991             type: 'heading',
16992             depth: cap[1].length,
16993             text: cap[2]
16994           });
16995           continue;
16996         }
16997     
16998         // table no leading pipe (gfm)
16999         if (top && (cap = this.rules.nptable.exec(src))) {
17000           src = src.substring(cap[0].length);
17001     
17002           item = {
17003             type: 'table',
17004             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17005             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17006             cells: cap[3].replace(/\n$/, '').split('\n')
17007           };
17008     
17009           for (i = 0; i < item.align.length; i++) {
17010             if (/^ *-+: *$/.test(item.align[i])) {
17011               item.align[i] = 'right';
17012             } else if (/^ *:-+: *$/.test(item.align[i])) {
17013               item.align[i] = 'center';
17014             } else if (/^ *:-+ *$/.test(item.align[i])) {
17015               item.align[i] = 'left';
17016             } else {
17017               item.align[i] = null;
17018             }
17019           }
17020     
17021           for (i = 0; i < item.cells.length; i++) {
17022             item.cells[i] = item.cells[i].split(/ *\| */);
17023           }
17024     
17025           this.tokens.push(item);
17026     
17027           continue;
17028         }
17029     
17030         // lheading
17031         if (cap = this.rules.lheading.exec(src)) {
17032           src = src.substring(cap[0].length);
17033           this.tokens.push({
17034             type: 'heading',
17035             depth: cap[2] === '=' ? 1 : 2,
17036             text: cap[1]
17037           });
17038           continue;
17039         }
17040     
17041         // hr
17042         if (cap = this.rules.hr.exec(src)) {
17043           src = src.substring(cap[0].length);
17044           this.tokens.push({
17045             type: 'hr'
17046           });
17047           continue;
17048         }
17049     
17050         // blockquote
17051         if (cap = this.rules.blockquote.exec(src)) {
17052           src = src.substring(cap[0].length);
17053     
17054           this.tokens.push({
17055             type: 'blockquote_start'
17056           });
17057     
17058           cap = cap[0].replace(/^ *> ?/gm, '');
17059     
17060           // Pass `top` to keep the current
17061           // "toplevel" state. This is exactly
17062           // how markdown.pl works.
17063           this.token(cap, top, true);
17064     
17065           this.tokens.push({
17066             type: 'blockquote_end'
17067           });
17068     
17069           continue;
17070         }
17071     
17072         // list
17073         if (cap = this.rules.list.exec(src)) {
17074           src = src.substring(cap[0].length);
17075           bull = cap[2];
17076     
17077           this.tokens.push({
17078             type: 'list_start',
17079             ordered: bull.length > 1
17080           });
17081     
17082           // Get each top-level item.
17083           cap = cap[0].match(this.rules.item);
17084     
17085           next = false;
17086           l = cap.length;
17087           i = 0;
17088     
17089           for (; i < l; i++) {
17090             item = cap[i];
17091     
17092             // Remove the list item's bullet
17093             // so it is seen as the next token.
17094             space = item.length;
17095             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17096     
17097             // Outdent whatever the
17098             // list item contains. Hacky.
17099             if (~item.indexOf('\n ')) {
17100               space -= item.length;
17101               item = !this.options.pedantic
17102                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17103                 : item.replace(/^ {1,4}/gm, '');
17104             }
17105     
17106             // Determine whether the next list item belongs here.
17107             // Backpedal if it does not belong in this list.
17108             if (this.options.smartLists && i !== l - 1) {
17109               b = block.bullet.exec(cap[i + 1])[0];
17110               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17111                 src = cap.slice(i + 1).join('\n') + src;
17112                 i = l - 1;
17113               }
17114             }
17115     
17116             // Determine whether item is loose or not.
17117             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17118             // for discount behavior.
17119             loose = next || /\n\n(?!\s*$)/.test(item);
17120             if (i !== l - 1) {
17121               next = item.charAt(item.length - 1) === '\n';
17122               if (!loose) { loose = next; }
17123             }
17124     
17125             this.tokens.push({
17126               type: loose
17127                 ? 'loose_item_start'
17128                 : 'list_item_start'
17129             });
17130     
17131             // Recurse.
17132             this.token(item, false, bq);
17133     
17134             this.tokens.push({
17135               type: 'list_item_end'
17136             });
17137           }
17138     
17139           this.tokens.push({
17140             type: 'list_end'
17141           });
17142     
17143           continue;
17144         }
17145     
17146         // html
17147         if (cap = this.rules.html.exec(src)) {
17148           src = src.substring(cap[0].length);
17149           this.tokens.push({
17150             type: this.options.sanitize
17151               ? 'paragraph'
17152               : 'html',
17153             pre: !this.options.sanitizer
17154               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17155             text: cap[0]
17156           });
17157           continue;
17158         }
17159     
17160         // def
17161         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17162           src = src.substring(cap[0].length);
17163           this.tokens.links[cap[1].toLowerCase()] = {
17164             href: cap[2],
17165             title: cap[3]
17166           };
17167           continue;
17168         }
17169     
17170         // table (gfm)
17171         if (top && (cap = this.rules.table.exec(src))) {
17172           src = src.substring(cap[0].length);
17173     
17174           item = {
17175             type: 'table',
17176             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17177             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17178             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17179           };
17180     
17181           for (i = 0; i < item.align.length; i++) {
17182             if (/^ *-+: *$/.test(item.align[i])) {
17183               item.align[i] = 'right';
17184             } else if (/^ *:-+: *$/.test(item.align[i])) {
17185               item.align[i] = 'center';
17186             } else if (/^ *:-+ *$/.test(item.align[i])) {
17187               item.align[i] = 'left';
17188             } else {
17189               item.align[i] = null;
17190             }
17191           }
17192     
17193           for (i = 0; i < item.cells.length; i++) {
17194             item.cells[i] = item.cells[i]
17195               .replace(/^ *\| *| *\| *$/g, '')
17196               .split(/ *\| */);
17197           }
17198     
17199           this.tokens.push(item);
17200     
17201           continue;
17202         }
17203     
17204         // top-level paragraph
17205         if (top && (cap = this.rules.paragraph.exec(src))) {
17206           src = src.substring(cap[0].length);
17207           this.tokens.push({
17208             type: 'paragraph',
17209             text: cap[1].charAt(cap[1].length - 1) === '\n'
17210               ? cap[1].slice(0, -1)
17211               : cap[1]
17212           });
17213           continue;
17214         }
17215     
17216         // text
17217         if (cap = this.rules.text.exec(src)) {
17218           // Top-level should never reach here.
17219           src = src.substring(cap[0].length);
17220           this.tokens.push({
17221             type: 'text',
17222             text: cap[0]
17223           });
17224           continue;
17225         }
17226     
17227         if (src) {
17228           throw new
17229             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17230         }
17231       }
17232     
17233       return this.tokens;
17234     };
17235     
17236     /**
17237      * Inline-Level Grammar
17238      */
17239     
17240     var inline = {
17241       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17242       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17243       url: noop,
17244       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17245       link: /^!?\[(inside)\]\(href\)/,
17246       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17247       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17248       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17249       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17250       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17251       br: /^ {2,}\n(?!\s*$)/,
17252       del: noop,
17253       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17254     };
17255     
17256     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17257     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17258     
17259     inline.link = replace(inline.link)
17260       ('inside', inline._inside)
17261       ('href', inline._href)
17262       ();
17263     
17264     inline.reflink = replace(inline.reflink)
17265       ('inside', inline._inside)
17266       ();
17267     
17268     /**
17269      * Normal Inline Grammar
17270      */
17271     
17272     inline.normal = merge({}, inline);
17273     
17274     /**
17275      * Pedantic Inline Grammar
17276      */
17277     
17278     inline.pedantic = merge({}, inline.normal, {
17279       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17280       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17281     });
17282     
17283     /**
17284      * GFM Inline Grammar
17285      */
17286     
17287     inline.gfm = merge({}, inline.normal, {
17288       escape: replace(inline.escape)('])', '~|])')(),
17289       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17290       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17291       text: replace(inline.text)
17292         (']|', '~]|')
17293         ('|', '|https?://|')
17294         ()
17295     });
17296     
17297     /**
17298      * GFM + Line Breaks Inline Grammar
17299      */
17300     
17301     inline.breaks = merge({}, inline.gfm, {
17302       br: replace(inline.br)('{2,}', '*')(),
17303       text: replace(inline.gfm.text)('{2,}', '*')()
17304     });
17305     
17306     /**
17307      * Inline Lexer & Compiler
17308      */
17309     
17310     function InlineLexer(links, options) {
17311       this.options = options || marked.defaults;
17312       this.links = links;
17313       this.rules = inline.normal;
17314       this.renderer = this.options.renderer || new Renderer;
17315       this.renderer.options = this.options;
17316     
17317       if (!this.links) {
17318         throw new
17319           Error('Tokens array requires a `links` property.');
17320       }
17321     
17322       if (this.options.gfm) {
17323         if (this.options.breaks) {
17324           this.rules = inline.breaks;
17325         } else {
17326           this.rules = inline.gfm;
17327         }
17328       } else if (this.options.pedantic) {
17329         this.rules = inline.pedantic;
17330       }
17331     }
17332     
17333     /**
17334      * Expose Inline Rules
17335      */
17336     
17337     InlineLexer.rules = inline;
17338     
17339     /**
17340      * Static Lexing/Compiling Method
17341      */
17342     
17343     InlineLexer.output = function(src, links, options) {
17344       var inline = new InlineLexer(links, options);
17345       return inline.output(src);
17346     };
17347     
17348     /**
17349      * Lexing/Compiling
17350      */
17351     
17352     InlineLexer.prototype.output = function(src) {
17353       var out = ''
17354         , link
17355         , text
17356         , href
17357         , cap;
17358     
17359       while (src) {
17360         // escape
17361         if (cap = this.rules.escape.exec(src)) {
17362           src = src.substring(cap[0].length);
17363           out += cap[1];
17364           continue;
17365         }
17366     
17367         // autolink
17368         if (cap = this.rules.autolink.exec(src)) {
17369           src = src.substring(cap[0].length);
17370           if (cap[2] === '@') {
17371             text = cap[1].charAt(6) === ':'
17372               ? this.mangle(cap[1].substring(7))
17373               : this.mangle(cap[1]);
17374             href = this.mangle('mailto:') + text;
17375           } else {
17376             text = escape(cap[1]);
17377             href = text;
17378           }
17379           out += this.renderer.link(href, null, text);
17380           continue;
17381         }
17382     
17383         // url (gfm)
17384         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17385           src = src.substring(cap[0].length);
17386           text = escape(cap[1]);
17387           href = text;
17388           out += this.renderer.link(href, null, text);
17389           continue;
17390         }
17391     
17392         // tag
17393         if (cap = this.rules.tag.exec(src)) {
17394           if (!this.inLink && /^<a /i.test(cap[0])) {
17395             this.inLink = true;
17396           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17397             this.inLink = false;
17398           }
17399           src = src.substring(cap[0].length);
17400           out += this.options.sanitize
17401             ? this.options.sanitizer
17402               ? this.options.sanitizer(cap[0])
17403               : escape(cap[0])
17404             : cap[0];
17405           continue;
17406         }
17407     
17408         // link
17409         if (cap = this.rules.link.exec(src)) {
17410           src = src.substring(cap[0].length);
17411           this.inLink = true;
17412           out += this.outputLink(cap, {
17413             href: cap[2],
17414             title: cap[3]
17415           });
17416           this.inLink = false;
17417           continue;
17418         }
17419     
17420         // reflink, nolink
17421         if ((cap = this.rules.reflink.exec(src))
17422             || (cap = this.rules.nolink.exec(src))) {
17423           src = src.substring(cap[0].length);
17424           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17425           link = this.links[link.toLowerCase()];
17426           if (!link || !link.href) {
17427             out += cap[0].charAt(0);
17428             src = cap[0].substring(1) + src;
17429             continue;
17430           }
17431           this.inLink = true;
17432           out += this.outputLink(cap, link);
17433           this.inLink = false;
17434           continue;
17435         }
17436     
17437         // strong
17438         if (cap = this.rules.strong.exec(src)) {
17439           src = src.substring(cap[0].length);
17440           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17441           continue;
17442         }
17443     
17444         // em
17445         if (cap = this.rules.em.exec(src)) {
17446           src = src.substring(cap[0].length);
17447           out += this.renderer.em(this.output(cap[2] || cap[1]));
17448           continue;
17449         }
17450     
17451         // code
17452         if (cap = this.rules.code.exec(src)) {
17453           src = src.substring(cap[0].length);
17454           out += this.renderer.codespan(escape(cap[2], true));
17455           continue;
17456         }
17457     
17458         // br
17459         if (cap = this.rules.br.exec(src)) {
17460           src = src.substring(cap[0].length);
17461           out += this.renderer.br();
17462           continue;
17463         }
17464     
17465         // del (gfm)
17466         if (cap = this.rules.del.exec(src)) {
17467           src = src.substring(cap[0].length);
17468           out += this.renderer.del(this.output(cap[1]));
17469           continue;
17470         }
17471     
17472         // text
17473         if (cap = this.rules.text.exec(src)) {
17474           src = src.substring(cap[0].length);
17475           out += this.renderer.text(escape(this.smartypants(cap[0])));
17476           continue;
17477         }
17478     
17479         if (src) {
17480           throw new
17481             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17482         }
17483       }
17484     
17485       return out;
17486     };
17487     
17488     /**
17489      * Compile Link
17490      */
17491     
17492     InlineLexer.prototype.outputLink = function(cap, link) {
17493       var href = escape(link.href)
17494         , title = link.title ? escape(link.title) : null;
17495     
17496       return cap[0].charAt(0) !== '!'
17497         ? this.renderer.link(href, title, this.output(cap[1]))
17498         : this.renderer.image(href, title, escape(cap[1]));
17499     };
17500     
17501     /**
17502      * Smartypants Transformations
17503      */
17504     
17505     InlineLexer.prototype.smartypants = function(text) {
17506       if (!this.options.smartypants)  { return text; }
17507       return text
17508         // em-dashes
17509         .replace(/---/g, '\u2014')
17510         // en-dashes
17511         .replace(/--/g, '\u2013')
17512         // opening singles
17513         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17514         // closing singles & apostrophes
17515         .replace(/'/g, '\u2019')
17516         // opening doubles
17517         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17518         // closing doubles
17519         .replace(/"/g, '\u201d')
17520         // ellipses
17521         .replace(/\.{3}/g, '\u2026');
17522     };
17523     
17524     /**
17525      * Mangle Links
17526      */
17527     
17528     InlineLexer.prototype.mangle = function(text) {
17529       if (!this.options.mangle) { return text; }
17530       var out = ''
17531         , l = text.length
17532         , i = 0
17533         , ch;
17534     
17535       for (; i < l; i++) {
17536         ch = text.charCodeAt(i);
17537         if (Math.random() > 0.5) {
17538           ch = 'x' + ch.toString(16);
17539         }
17540         out += '&#' + ch + ';';
17541       }
17542     
17543       return out;
17544     };
17545     
17546     /**
17547      * Renderer
17548      */
17549     
17550     function Renderer(options) {
17551       this.options = options || {};
17552     }
17553     
17554     Renderer.prototype.code = function(code, lang, escaped) {
17555       if (this.options.highlight) {
17556         var out = this.options.highlight(code, lang);
17557         if (out != null && out !== code) {
17558           escaped = true;
17559           code = out;
17560         }
17561       } else {
17562             // hack!!! - it's already escapeD?
17563             escaped = true;
17564       }
17565     
17566       if (!lang) {
17567         return '<pre><code>'
17568           + (escaped ? code : escape(code, true))
17569           + '\n</code></pre>';
17570       }
17571     
17572       return '<pre><code class="'
17573         + this.options.langPrefix
17574         + escape(lang, true)
17575         + '">'
17576         + (escaped ? code : escape(code, true))
17577         + '\n</code></pre>\n';
17578     };
17579     
17580     Renderer.prototype.blockquote = function(quote) {
17581       return '<blockquote>\n' + quote + '</blockquote>\n';
17582     };
17583     
17584     Renderer.prototype.html = function(html) {
17585       return html;
17586     };
17587     
17588     Renderer.prototype.heading = function(text, level, raw) {
17589       return '<h'
17590         + level
17591         + ' id="'
17592         + this.options.headerPrefix
17593         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17594         + '">'
17595         + text
17596         + '</h'
17597         + level
17598         + '>\n';
17599     };
17600     
17601     Renderer.prototype.hr = function() {
17602       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17603     };
17604     
17605     Renderer.prototype.list = function(body, ordered) {
17606       var type = ordered ? 'ol' : 'ul';
17607       return '<' + type + '>\n' + body + '</' + type + '>\n';
17608     };
17609     
17610     Renderer.prototype.listitem = function(text) {
17611       return '<li>' + text + '</li>\n';
17612     };
17613     
17614     Renderer.prototype.paragraph = function(text) {
17615       return '<p>' + text + '</p>\n';
17616     };
17617     
17618     Renderer.prototype.table = function(header, body) {
17619       return '<table class="table table-striped">\n'
17620         + '<thead>\n'
17621         + header
17622         + '</thead>\n'
17623         + '<tbody>\n'
17624         + body
17625         + '</tbody>\n'
17626         + '</table>\n';
17627     };
17628     
17629     Renderer.prototype.tablerow = function(content) {
17630       return '<tr>\n' + content + '</tr>\n';
17631     };
17632     
17633     Renderer.prototype.tablecell = function(content, flags) {
17634       var type = flags.header ? 'th' : 'td';
17635       var tag = flags.align
17636         ? '<' + type + ' style="text-align:' + flags.align + '">'
17637         : '<' + type + '>';
17638       return tag + content + '</' + type + '>\n';
17639     };
17640     
17641     // span level renderer
17642     Renderer.prototype.strong = function(text) {
17643       return '<strong>' + text + '</strong>';
17644     };
17645     
17646     Renderer.prototype.em = function(text) {
17647       return '<em>' + text + '</em>';
17648     };
17649     
17650     Renderer.prototype.codespan = function(text) {
17651       return '<code>' + text + '</code>';
17652     };
17653     
17654     Renderer.prototype.br = function() {
17655       return this.options.xhtml ? '<br/>' : '<br>';
17656     };
17657     
17658     Renderer.prototype.del = function(text) {
17659       return '<del>' + text + '</del>';
17660     };
17661     
17662     Renderer.prototype.link = function(href, title, text) {
17663       if (this.options.sanitize) {
17664         try {
17665           var prot = decodeURIComponent(unescape(href))
17666             .replace(/[^\w:]/g, '')
17667             .toLowerCase();
17668         } catch (e) {
17669           return '';
17670         }
17671         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17672           return '';
17673         }
17674       }
17675       var out = '<a href="' + href + '"';
17676       if (title) {
17677         out += ' title="' + title + '"';
17678       }
17679       out += '>' + text + '</a>';
17680       return out;
17681     };
17682     
17683     Renderer.prototype.image = function(href, title, text) {
17684       var out = '<img src="' + href + '" alt="' + text + '"';
17685       if (title) {
17686         out += ' title="' + title + '"';
17687       }
17688       out += this.options.xhtml ? '/>' : '>';
17689       return out;
17690     };
17691     
17692     Renderer.prototype.text = function(text) {
17693       return text;
17694     };
17695     
17696     /**
17697      * Parsing & Compiling
17698      */
17699     
17700     function Parser(options) {
17701       this.tokens = [];
17702       this.token = null;
17703       this.options = options || marked.defaults;
17704       this.options.renderer = this.options.renderer || new Renderer;
17705       this.renderer = this.options.renderer;
17706       this.renderer.options = this.options;
17707     }
17708     
17709     /**
17710      * Static Parse Method
17711      */
17712     
17713     Parser.parse = function(src, options, renderer) {
17714       var parser = new Parser(options, renderer);
17715       return parser.parse(src);
17716     };
17717     
17718     /**
17719      * Parse Loop
17720      */
17721     
17722     Parser.prototype.parse = function(src) {
17723       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17724       this.tokens = src.reverse();
17725     
17726       var out = '';
17727       while (this.next()) {
17728         out += this.tok();
17729       }
17730     
17731       return out;
17732     };
17733     
17734     /**
17735      * Next Token
17736      */
17737     
17738     Parser.prototype.next = function() {
17739       return this.token = this.tokens.pop();
17740     };
17741     
17742     /**
17743      * Preview Next Token
17744      */
17745     
17746     Parser.prototype.peek = function() {
17747       return this.tokens[this.tokens.length - 1] || 0;
17748     };
17749     
17750     /**
17751      * Parse Text Tokens
17752      */
17753     
17754     Parser.prototype.parseText = function() {
17755       var body = this.token.text;
17756     
17757       while (this.peek().type === 'text') {
17758         body += '\n' + this.next().text;
17759       }
17760     
17761       return this.inline.output(body);
17762     };
17763     
17764     /**
17765      * Parse Current Token
17766      */
17767     
17768     Parser.prototype.tok = function() {
17769       switch (this.token.type) {
17770         case 'space': {
17771           return '';
17772         }
17773         case 'hr': {
17774           return this.renderer.hr();
17775         }
17776         case 'heading': {
17777           return this.renderer.heading(
17778             this.inline.output(this.token.text),
17779             this.token.depth,
17780             this.token.text);
17781         }
17782         case 'code': {
17783           return this.renderer.code(this.token.text,
17784             this.token.lang,
17785             this.token.escaped);
17786         }
17787         case 'table': {
17788           var header = ''
17789             , body = ''
17790             , i
17791             , row
17792             , cell
17793             , flags
17794             , j;
17795     
17796           // header
17797           cell = '';
17798           for (i = 0; i < this.token.header.length; i++) {
17799             flags = { header: true, align: this.token.align[i] };
17800             cell += this.renderer.tablecell(
17801               this.inline.output(this.token.header[i]),
17802               { header: true, align: this.token.align[i] }
17803             );
17804           }
17805           header += this.renderer.tablerow(cell);
17806     
17807           for (i = 0; i < this.token.cells.length; i++) {
17808             row = this.token.cells[i];
17809     
17810             cell = '';
17811             for (j = 0; j < row.length; j++) {
17812               cell += this.renderer.tablecell(
17813                 this.inline.output(row[j]),
17814                 { header: false, align: this.token.align[j] }
17815               );
17816             }
17817     
17818             body += this.renderer.tablerow(cell);
17819           }
17820           return this.renderer.table(header, body);
17821         }
17822         case 'blockquote_start': {
17823           var body = '';
17824     
17825           while (this.next().type !== 'blockquote_end') {
17826             body += this.tok();
17827           }
17828     
17829           return this.renderer.blockquote(body);
17830         }
17831         case 'list_start': {
17832           var body = ''
17833             , ordered = this.token.ordered;
17834     
17835           while (this.next().type !== 'list_end') {
17836             body += this.tok();
17837           }
17838     
17839           return this.renderer.list(body, ordered);
17840         }
17841         case 'list_item_start': {
17842           var body = '';
17843     
17844           while (this.next().type !== 'list_item_end') {
17845             body += this.token.type === 'text'
17846               ? this.parseText()
17847               : this.tok();
17848           }
17849     
17850           return this.renderer.listitem(body);
17851         }
17852         case 'loose_item_start': {
17853           var body = '';
17854     
17855           while (this.next().type !== 'list_item_end') {
17856             body += this.tok();
17857           }
17858     
17859           return this.renderer.listitem(body);
17860         }
17861         case 'html': {
17862           var html = !this.token.pre && !this.options.pedantic
17863             ? this.inline.output(this.token.text)
17864             : this.token.text;
17865           return this.renderer.html(html);
17866         }
17867         case 'paragraph': {
17868           return this.renderer.paragraph(this.inline.output(this.token.text));
17869         }
17870         case 'text': {
17871           return this.renderer.paragraph(this.parseText());
17872         }
17873       }
17874     };
17875     
17876     /**
17877      * Helpers
17878      */
17879     
17880     function escape(html, encode) {
17881       return html
17882         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17883         .replace(/</g, '&lt;')
17884         .replace(/>/g, '&gt;')
17885         .replace(/"/g, '&quot;')
17886         .replace(/'/g, '&#39;');
17887     }
17888     
17889     function unescape(html) {
17890         // explicitly match decimal, hex, and named HTML entities 
17891       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17892         n = n.toLowerCase();
17893         if (n === 'colon') { return ':'; }
17894         if (n.charAt(0) === '#') {
17895           return n.charAt(1) === 'x'
17896             ? String.fromCharCode(parseInt(n.substring(2), 16))
17897             : String.fromCharCode(+n.substring(1));
17898         }
17899         return '';
17900       });
17901     }
17902     
17903     function replace(regex, opt) {
17904       regex = regex.source;
17905       opt = opt || '';
17906       return function self(name, val) {
17907         if (!name) { return new RegExp(regex, opt); }
17908         val = val.source || val;
17909         val = val.replace(/(^|[^\[])\^/g, '$1');
17910         regex = regex.replace(name, val);
17911         return self;
17912       };
17913     }
17914     
17915     function noop() {}
17916     noop.exec = noop;
17917     
17918     function merge(obj) {
17919       var i = 1
17920         , target
17921         , key;
17922     
17923       for (; i < arguments.length; i++) {
17924         target = arguments[i];
17925         for (key in target) {
17926           if (Object.prototype.hasOwnProperty.call(target, key)) {
17927             obj[key] = target[key];
17928           }
17929         }
17930       }
17931     
17932       return obj;
17933     }
17934     
17935     
17936     /**
17937      * Marked
17938      */
17939     
17940     function marked(src, opt, callback) {
17941       if (callback || typeof opt === 'function') {
17942         if (!callback) {
17943           callback = opt;
17944           opt = null;
17945         }
17946     
17947         opt = merge({}, marked.defaults, opt || {});
17948     
17949         var highlight = opt.highlight
17950           , tokens
17951           , pending
17952           , i = 0;
17953     
17954         try {
17955           tokens = Lexer.lex(src, opt)
17956         } catch (e) {
17957           return callback(e);
17958         }
17959     
17960         pending = tokens.length;
17961     
17962         var done = function(err) {
17963           if (err) {
17964             opt.highlight = highlight;
17965             return callback(err);
17966           }
17967     
17968           var out;
17969     
17970           try {
17971             out = Parser.parse(tokens, opt);
17972           } catch (e) {
17973             err = e;
17974           }
17975     
17976           opt.highlight = highlight;
17977     
17978           return err
17979             ? callback(err)
17980             : callback(null, out);
17981         };
17982     
17983         if (!highlight || highlight.length < 3) {
17984           return done();
17985         }
17986     
17987         delete opt.highlight;
17988     
17989         if (!pending) { return done(); }
17990     
17991         for (; i < tokens.length; i++) {
17992           (function(token) {
17993             if (token.type !== 'code') {
17994               return --pending || done();
17995             }
17996             return highlight(token.text, token.lang, function(err, code) {
17997               if (err) { return done(err); }
17998               if (code == null || code === token.text) {
17999                 return --pending || done();
18000               }
18001               token.text = code;
18002               token.escaped = true;
18003               --pending || done();
18004             });
18005           })(tokens[i]);
18006         }
18007     
18008         return;
18009       }
18010       try {
18011         if (opt) { opt = merge({}, marked.defaults, opt); }
18012         return Parser.parse(Lexer.lex(src, opt), opt);
18013       } catch (e) {
18014         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18015         if ((opt || marked.defaults).silent) {
18016           return '<p>An error occured:</p><pre>'
18017             + escape(e.message + '', true)
18018             + '</pre>';
18019         }
18020         throw e;
18021       }
18022     }
18023     
18024     /**
18025      * Options
18026      */
18027     
18028     marked.options =
18029     marked.setOptions = function(opt) {
18030       merge(marked.defaults, opt);
18031       return marked;
18032     };
18033     
18034     marked.defaults = {
18035       gfm: true,
18036       tables: true,
18037       breaks: false,
18038       pedantic: false,
18039       sanitize: false,
18040       sanitizer: null,
18041       mangle: true,
18042       smartLists: false,
18043       silent: false,
18044       highlight: null,
18045       langPrefix: 'lang-',
18046       smartypants: false,
18047       headerPrefix: '',
18048       renderer: new Renderer,
18049       xhtml: false
18050     };
18051     
18052     /**
18053      * Expose
18054      */
18055     
18056     marked.Parser = Parser;
18057     marked.parser = Parser.parse;
18058     
18059     marked.Renderer = Renderer;
18060     
18061     marked.Lexer = Lexer;
18062     marked.lexer = Lexer.lex;
18063     
18064     marked.InlineLexer = InlineLexer;
18065     marked.inlineLexer = InlineLexer.output;
18066     
18067     marked.parse = marked;
18068     
18069     Roo.Markdown.marked = marked;
18070
18071 })();/*
18072  * Based on:
18073  * Ext JS Library 1.1.1
18074  * Copyright(c) 2006-2007, Ext JS, LLC.
18075  *
18076  * Originally Released Under LGPL - original licence link has changed is not relivant.
18077  *
18078  * Fork - LGPL
18079  * <script type="text/javascript">
18080  */
18081
18082
18083
18084 /*
18085  * These classes are derivatives of the similarly named classes in the YUI Library.
18086  * The original license:
18087  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18088  * Code licensed under the BSD License:
18089  * http://developer.yahoo.net/yui/license.txt
18090  */
18091
18092 (function() {
18093
18094 var Event=Roo.EventManager;
18095 var Dom=Roo.lib.Dom;
18096
18097 /**
18098  * @class Roo.dd.DragDrop
18099  * @extends Roo.util.Observable
18100  * Defines the interface and base operation of items that that can be
18101  * dragged or can be drop targets.  It was designed to be extended, overriding
18102  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18103  * Up to three html elements can be associated with a DragDrop instance:
18104  * <ul>
18105  * <li>linked element: the element that is passed into the constructor.
18106  * This is the element which defines the boundaries for interaction with
18107  * other DragDrop objects.</li>
18108  * <li>handle element(s): The drag operation only occurs if the element that
18109  * was clicked matches a handle element.  By default this is the linked
18110  * element, but there are times that you will want only a portion of the
18111  * linked element to initiate the drag operation, and the setHandleElId()
18112  * method provides a way to define this.</li>
18113  * <li>drag element: this represents the element that would be moved along
18114  * with the cursor during a drag operation.  By default, this is the linked
18115  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18116  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18117  * </li>
18118  * </ul>
18119  * This class should not be instantiated until the onload event to ensure that
18120  * the associated elements are available.
18121  * The following would define a DragDrop obj that would interact with any
18122  * other DragDrop obj in the "group1" group:
18123  * <pre>
18124  *  dd = new Roo.dd.DragDrop("div1", "group1");
18125  * </pre>
18126  * Since none of the event handlers have been implemented, nothing would
18127  * actually happen if you were to run the code above.  Normally you would
18128  * override this class or one of the default implementations, but you can
18129  * also override the methods you want on an instance of the class...
18130  * <pre>
18131  *  dd.onDragDrop = function(e, id) {
18132  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18133  *  }
18134  * </pre>
18135  * @constructor
18136  * @param {String} id of the element that is linked to this instance
18137  * @param {String} sGroup the group of related DragDrop objects
18138  * @param {object} config an object containing configurable attributes
18139  *                Valid properties for DragDrop:
18140  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18141  */
18142 Roo.dd.DragDrop = function(id, sGroup, config) {
18143     if (id) {
18144         this.init(id, sGroup, config);
18145     }
18146     
18147 };
18148
18149 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18150
18151     /**
18152      * The id of the element associated with this object.  This is what we
18153      * refer to as the "linked element" because the size and position of
18154      * this element is used to determine when the drag and drop objects have
18155      * interacted.
18156      * @property id
18157      * @type String
18158      */
18159     id: null,
18160
18161     /**
18162      * Configuration attributes passed into the constructor
18163      * @property config
18164      * @type object
18165      */
18166     config: null,
18167
18168     /**
18169      * The id of the element that will be dragged.  By default this is same
18170      * as the linked element , but could be changed to another element. Ex:
18171      * Roo.dd.DDProxy
18172      * @property dragElId
18173      * @type String
18174      * @private
18175      */
18176     dragElId: null,
18177
18178     /**
18179      * the id of the element that initiates the drag operation.  By default
18180      * this is the linked element, but could be changed to be a child of this
18181      * element.  This lets us do things like only starting the drag when the
18182      * header element within the linked html element is clicked.
18183      * @property handleElId
18184      * @type String
18185      * @private
18186      */
18187     handleElId: null,
18188
18189     /**
18190      * An associative array of HTML tags that will be ignored if clicked.
18191      * @property invalidHandleTypes
18192      * @type {string: string}
18193      */
18194     invalidHandleTypes: null,
18195
18196     /**
18197      * An associative array of ids for elements that will be ignored if clicked
18198      * @property invalidHandleIds
18199      * @type {string: string}
18200      */
18201     invalidHandleIds: null,
18202
18203     /**
18204      * An indexted array of css class names for elements that will be ignored
18205      * if clicked.
18206      * @property invalidHandleClasses
18207      * @type string[]
18208      */
18209     invalidHandleClasses: null,
18210
18211     /**
18212      * The linked element's absolute X position at the time the drag was
18213      * started
18214      * @property startPageX
18215      * @type int
18216      * @private
18217      */
18218     startPageX: 0,
18219
18220     /**
18221      * The linked element's absolute X position at the time the drag was
18222      * started
18223      * @property startPageY
18224      * @type int
18225      * @private
18226      */
18227     startPageY: 0,
18228
18229     /**
18230      * The group defines a logical collection of DragDrop objects that are
18231      * related.  Instances only get events when interacting with other
18232      * DragDrop object in the same group.  This lets us define multiple
18233      * groups using a single DragDrop subclass if we want.
18234      * @property groups
18235      * @type {string: string}
18236      */
18237     groups: null,
18238
18239     /**
18240      * Individual drag/drop instances can be locked.  This will prevent
18241      * onmousedown start drag.
18242      * @property locked
18243      * @type boolean
18244      * @private
18245      */
18246     locked: false,
18247
18248     /**
18249      * Lock this instance
18250      * @method lock
18251      */
18252     lock: function() { this.locked = true; },
18253
18254     /**
18255      * Unlock this instace
18256      * @method unlock
18257      */
18258     unlock: function() { this.locked = false; },
18259
18260     /**
18261      * By default, all insances can be a drop target.  This can be disabled by
18262      * setting isTarget to false.
18263      * @method isTarget
18264      * @type boolean
18265      */
18266     isTarget: true,
18267
18268     /**
18269      * The padding configured for this drag and drop object for calculating
18270      * the drop zone intersection with this object.
18271      * @method padding
18272      * @type int[]
18273      */
18274     padding: null,
18275
18276     /**
18277      * Cached reference to the linked element
18278      * @property _domRef
18279      * @private
18280      */
18281     _domRef: null,
18282
18283     /**
18284      * Internal typeof flag
18285      * @property __ygDragDrop
18286      * @private
18287      */
18288     __ygDragDrop: true,
18289
18290     /**
18291      * Set to true when horizontal contraints are applied
18292      * @property constrainX
18293      * @type boolean
18294      * @private
18295      */
18296     constrainX: false,
18297
18298     /**
18299      * Set to true when vertical contraints are applied
18300      * @property constrainY
18301      * @type boolean
18302      * @private
18303      */
18304     constrainY: false,
18305
18306     /**
18307      * The left constraint
18308      * @property minX
18309      * @type int
18310      * @private
18311      */
18312     minX: 0,
18313
18314     /**
18315      * The right constraint
18316      * @property maxX
18317      * @type int
18318      * @private
18319      */
18320     maxX: 0,
18321
18322     /**
18323      * The up constraint
18324      * @property minY
18325      * @type int
18326      * @type int
18327      * @private
18328      */
18329     minY: 0,
18330
18331     /**
18332      * The down constraint
18333      * @property maxY
18334      * @type int
18335      * @private
18336      */
18337     maxY: 0,
18338
18339     /**
18340      * Maintain offsets when we resetconstraints.  Set to true when you want
18341      * the position of the element relative to its parent to stay the same
18342      * when the page changes
18343      *
18344      * @property maintainOffset
18345      * @type boolean
18346      */
18347     maintainOffset: false,
18348
18349     /**
18350      * Array of pixel locations the element will snap to if we specified a
18351      * horizontal graduation/interval.  This array is generated automatically
18352      * when you define a tick interval.
18353      * @property xTicks
18354      * @type int[]
18355      */
18356     xTicks: null,
18357
18358     /**
18359      * Array of pixel locations the element will snap to if we specified a
18360      * vertical graduation/interval.  This array is generated automatically
18361      * when you define a tick interval.
18362      * @property yTicks
18363      * @type int[]
18364      */
18365     yTicks: null,
18366
18367     /**
18368      * By default the drag and drop instance will only respond to the primary
18369      * button click (left button for a right-handed mouse).  Set to true to
18370      * allow drag and drop to start with any mouse click that is propogated
18371      * by the browser
18372      * @property primaryButtonOnly
18373      * @type boolean
18374      */
18375     primaryButtonOnly: true,
18376
18377     /**
18378      * The availabe property is false until the linked dom element is accessible.
18379      * @property available
18380      * @type boolean
18381      */
18382     available: false,
18383
18384     /**
18385      * By default, drags can only be initiated if the mousedown occurs in the
18386      * region the linked element is.  This is done in part to work around a
18387      * bug in some browsers that mis-report the mousedown if the previous
18388      * mouseup happened outside of the window.  This property is set to true
18389      * if outer handles are defined.
18390      *
18391      * @property hasOuterHandles
18392      * @type boolean
18393      * @default false
18394      */
18395     hasOuterHandles: false,
18396
18397     /**
18398      * Code that executes immediately before the startDrag event
18399      * @method b4StartDrag
18400      * @private
18401      */
18402     b4StartDrag: function(x, y) { },
18403
18404     /**
18405      * Abstract method called after a drag/drop object is clicked
18406      * and the drag or mousedown time thresholds have beeen met.
18407      * @method startDrag
18408      * @param {int} X click location
18409      * @param {int} Y click location
18410      */
18411     startDrag: function(x, y) { /* override this */ },
18412
18413     /**
18414      * Code that executes immediately before the onDrag event
18415      * @method b4Drag
18416      * @private
18417      */
18418     b4Drag: function(e) { },
18419
18420     /**
18421      * Abstract method called during the onMouseMove event while dragging an
18422      * object.
18423      * @method onDrag
18424      * @param {Event} e the mousemove event
18425      */
18426     onDrag: function(e) { /* override this */ },
18427
18428     /**
18429      * Abstract method called when this element fist begins hovering over
18430      * another DragDrop obj
18431      * @method onDragEnter
18432      * @param {Event} e the mousemove event
18433      * @param {String|DragDrop[]} id In POINT mode, the element
18434      * id this is hovering over.  In INTERSECT mode, an array of one or more
18435      * dragdrop items being hovered over.
18436      */
18437     onDragEnter: function(e, id) { /* override this */ },
18438
18439     /**
18440      * Code that executes immediately before the onDragOver event
18441      * @method b4DragOver
18442      * @private
18443      */
18444     b4DragOver: function(e) { },
18445
18446     /**
18447      * Abstract method called when this element is hovering over another
18448      * DragDrop obj
18449      * @method onDragOver
18450      * @param {Event} e the mousemove event
18451      * @param {String|DragDrop[]} id In POINT mode, the element
18452      * id this is hovering over.  In INTERSECT mode, an array of dd items
18453      * being hovered over.
18454      */
18455     onDragOver: function(e, id) { /* override this */ },
18456
18457     /**
18458      * Code that executes immediately before the onDragOut event
18459      * @method b4DragOut
18460      * @private
18461      */
18462     b4DragOut: function(e) { },
18463
18464     /**
18465      * Abstract method called when we are no longer hovering over an element
18466      * @method onDragOut
18467      * @param {Event} e the mousemove event
18468      * @param {String|DragDrop[]} id In POINT mode, the element
18469      * id this was hovering over.  In INTERSECT mode, an array of dd items
18470      * that the mouse is no longer over.
18471      */
18472     onDragOut: function(e, id) { /* override this */ },
18473
18474     /**
18475      * Code that executes immediately before the onDragDrop event
18476      * @method b4DragDrop
18477      * @private
18478      */
18479     b4DragDrop: function(e) { },
18480
18481     /**
18482      * Abstract method called when this item is dropped on another DragDrop
18483      * obj
18484      * @method onDragDrop
18485      * @param {Event} e the mouseup event
18486      * @param {String|DragDrop[]} id In POINT mode, the element
18487      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18488      * was dropped on.
18489      */
18490     onDragDrop: function(e, id) { /* override this */ },
18491
18492     /**
18493      * Abstract method called when this item is dropped on an area with no
18494      * drop target
18495      * @method onInvalidDrop
18496      * @param {Event} e the mouseup event
18497      */
18498     onInvalidDrop: function(e) { /* override this */ },
18499
18500     /**
18501      * Code that executes immediately before the endDrag event
18502      * @method b4EndDrag
18503      * @private
18504      */
18505     b4EndDrag: function(e) { },
18506
18507     /**
18508      * Fired when we are done dragging the object
18509      * @method endDrag
18510      * @param {Event} e the mouseup event
18511      */
18512     endDrag: function(e) { /* override this */ },
18513
18514     /**
18515      * Code executed immediately before the onMouseDown event
18516      * @method b4MouseDown
18517      * @param {Event} e the mousedown event
18518      * @private
18519      */
18520     b4MouseDown: function(e) {  },
18521
18522     /**
18523      * Event handler that fires when a drag/drop obj gets a mousedown
18524      * @method onMouseDown
18525      * @param {Event} e the mousedown event
18526      */
18527     onMouseDown: function(e) { /* override this */ },
18528
18529     /**
18530      * Event handler that fires when a drag/drop obj gets a mouseup
18531      * @method onMouseUp
18532      * @param {Event} e the mouseup event
18533      */
18534     onMouseUp: function(e) { /* override this */ },
18535
18536     /**
18537      * Override the onAvailable method to do what is needed after the initial
18538      * position was determined.
18539      * @method onAvailable
18540      */
18541     onAvailable: function () {
18542     },
18543
18544     /*
18545      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18546      * @type Object
18547      */
18548     defaultPadding : {left:0, right:0, top:0, bottom:0},
18549
18550     /*
18551      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18552  *
18553  * Usage:
18554  <pre><code>
18555  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18556                 { dragElId: "existingProxyDiv" });
18557  dd.startDrag = function(){
18558      this.constrainTo("parent-id");
18559  };
18560  </code></pre>
18561  * Or you can initalize it using the {@link Roo.Element} object:
18562  <pre><code>
18563  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18564      startDrag : function(){
18565          this.constrainTo("parent-id");
18566      }
18567  });
18568  </code></pre>
18569      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18570      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18571      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18572      * an object containing the sides to pad. For example: {right:10, bottom:10}
18573      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18574      */
18575     constrainTo : function(constrainTo, pad, inContent){
18576         if(typeof pad == "number"){
18577             pad = {left: pad, right:pad, top:pad, bottom:pad};
18578         }
18579         pad = pad || this.defaultPadding;
18580         var b = Roo.get(this.getEl()).getBox();
18581         var ce = Roo.get(constrainTo);
18582         var s = ce.getScroll();
18583         var c, cd = ce.dom;
18584         if(cd == document.body){
18585             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18586         }else{
18587             xy = ce.getXY();
18588             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18589         }
18590
18591
18592         var topSpace = b.y - c.y;
18593         var leftSpace = b.x - c.x;
18594
18595         this.resetConstraints();
18596         this.setXConstraint(leftSpace - (pad.left||0), // left
18597                 c.width - leftSpace - b.width - (pad.right||0) //right
18598         );
18599         this.setYConstraint(topSpace - (pad.top||0), //top
18600                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18601         );
18602     },
18603
18604     /**
18605      * Returns a reference to the linked element
18606      * @method getEl
18607      * @return {HTMLElement} the html element
18608      */
18609     getEl: function() {
18610         if (!this._domRef) {
18611             this._domRef = Roo.getDom(this.id);
18612         }
18613
18614         return this._domRef;
18615     },
18616
18617     /**
18618      * Returns a reference to the actual element to drag.  By default this is
18619      * the same as the html element, but it can be assigned to another
18620      * element. An example of this can be found in Roo.dd.DDProxy
18621      * @method getDragEl
18622      * @return {HTMLElement} the html element
18623      */
18624     getDragEl: function() {
18625         return Roo.getDom(this.dragElId);
18626     },
18627
18628     /**
18629      * Sets up the DragDrop object.  Must be called in the constructor of any
18630      * Roo.dd.DragDrop subclass
18631      * @method init
18632      * @param id the id of the linked element
18633      * @param {String} sGroup the group of related items
18634      * @param {object} config configuration attributes
18635      */
18636     init: function(id, sGroup, config) {
18637         this.initTarget(id, sGroup, config);
18638         if (!Roo.isTouch) {
18639             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18640         }
18641         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18642         // Event.on(this.id, "selectstart", Event.preventDefault);
18643     },
18644
18645     /**
18646      * Initializes Targeting functionality only... the object does not
18647      * get a mousedown handler.
18648      * @method initTarget
18649      * @param id the id of the linked element
18650      * @param {String} sGroup the group of related items
18651      * @param {object} config configuration attributes
18652      */
18653     initTarget: function(id, sGroup, config) {
18654
18655         // configuration attributes
18656         this.config = config || {};
18657
18658         // create a local reference to the drag and drop manager
18659         this.DDM = Roo.dd.DDM;
18660         // initialize the groups array
18661         this.groups = {};
18662
18663         // assume that we have an element reference instead of an id if the
18664         // parameter is not a string
18665         if (typeof id !== "string") {
18666             id = Roo.id(id);
18667         }
18668
18669         // set the id
18670         this.id = id;
18671
18672         // add to an interaction group
18673         this.addToGroup((sGroup) ? sGroup : "default");
18674
18675         // We don't want to register this as the handle with the manager
18676         // so we just set the id rather than calling the setter.
18677         this.handleElId = id;
18678
18679         // the linked element is the element that gets dragged by default
18680         this.setDragElId(id);
18681
18682         // by default, clicked anchors will not start drag operations.
18683         this.invalidHandleTypes = { A: "A" };
18684         this.invalidHandleIds = {};
18685         this.invalidHandleClasses = [];
18686
18687         this.applyConfig();
18688
18689         this.handleOnAvailable();
18690     },
18691
18692     /**
18693      * Applies the configuration parameters that were passed into the constructor.
18694      * This is supposed to happen at each level through the inheritance chain.  So
18695      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18696      * DragDrop in order to get all of the parameters that are available in
18697      * each object.
18698      * @method applyConfig
18699      */
18700     applyConfig: function() {
18701
18702         // configurable properties:
18703         //    padding, isTarget, maintainOffset, primaryButtonOnly
18704         this.padding           = this.config.padding || [0, 0, 0, 0];
18705         this.isTarget          = (this.config.isTarget !== false);
18706         this.maintainOffset    = (this.config.maintainOffset);
18707         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18708
18709     },
18710
18711     /**
18712      * Executed when the linked element is available
18713      * @method handleOnAvailable
18714      * @private
18715      */
18716     handleOnAvailable: function() {
18717         this.available = true;
18718         this.resetConstraints();
18719         this.onAvailable();
18720     },
18721
18722      /**
18723      * Configures the padding for the target zone in px.  Effectively expands
18724      * (or reduces) the virtual object size for targeting calculations.
18725      * Supports css-style shorthand; if only one parameter is passed, all sides
18726      * will have that padding, and if only two are passed, the top and bottom
18727      * will have the first param, the left and right the second.
18728      * @method setPadding
18729      * @param {int} iTop    Top pad
18730      * @param {int} iRight  Right pad
18731      * @param {int} iBot    Bot pad
18732      * @param {int} iLeft   Left pad
18733      */
18734     setPadding: function(iTop, iRight, iBot, iLeft) {
18735         // this.padding = [iLeft, iRight, iTop, iBot];
18736         if (!iRight && 0 !== iRight) {
18737             this.padding = [iTop, iTop, iTop, iTop];
18738         } else if (!iBot && 0 !== iBot) {
18739             this.padding = [iTop, iRight, iTop, iRight];
18740         } else {
18741             this.padding = [iTop, iRight, iBot, iLeft];
18742         }
18743     },
18744
18745     /**
18746      * Stores the initial placement of the linked element.
18747      * @method setInitialPosition
18748      * @param {int} diffX   the X offset, default 0
18749      * @param {int} diffY   the Y offset, default 0
18750      */
18751     setInitPosition: function(diffX, diffY) {
18752         var el = this.getEl();
18753
18754         if (!this.DDM.verifyEl(el)) {
18755             return;
18756         }
18757
18758         var dx = diffX || 0;
18759         var dy = diffY || 0;
18760
18761         var p = Dom.getXY( el );
18762
18763         this.initPageX = p[0] - dx;
18764         this.initPageY = p[1] - dy;
18765
18766         this.lastPageX = p[0];
18767         this.lastPageY = p[1];
18768
18769
18770         this.setStartPosition(p);
18771     },
18772
18773     /**
18774      * Sets the start position of the element.  This is set when the obj
18775      * is initialized, the reset when a drag is started.
18776      * @method setStartPosition
18777      * @param pos current position (from previous lookup)
18778      * @private
18779      */
18780     setStartPosition: function(pos) {
18781         var p = pos || Dom.getXY( this.getEl() );
18782         this.deltaSetXY = null;
18783
18784         this.startPageX = p[0];
18785         this.startPageY = p[1];
18786     },
18787
18788     /**
18789      * Add this instance to a group of related drag/drop objects.  All
18790      * instances belong to at least one group, and can belong to as many
18791      * groups as needed.
18792      * @method addToGroup
18793      * @param sGroup {string} the name of the group
18794      */
18795     addToGroup: function(sGroup) {
18796         this.groups[sGroup] = true;
18797         this.DDM.regDragDrop(this, sGroup);
18798     },
18799
18800     /**
18801      * Remove's this instance from the supplied interaction group
18802      * @method removeFromGroup
18803      * @param {string}  sGroup  The group to drop
18804      */
18805     removeFromGroup: function(sGroup) {
18806         if (this.groups[sGroup]) {
18807             delete this.groups[sGroup];
18808         }
18809
18810         this.DDM.removeDDFromGroup(this, sGroup);
18811     },
18812
18813     /**
18814      * Allows you to specify that an element other than the linked element
18815      * will be moved with the cursor during a drag
18816      * @method setDragElId
18817      * @param id {string} the id of the element that will be used to initiate the drag
18818      */
18819     setDragElId: function(id) {
18820         this.dragElId = id;
18821     },
18822
18823     /**
18824      * Allows you to specify a child of the linked element that should be
18825      * used to initiate the drag operation.  An example of this would be if
18826      * you have a content div with text and links.  Clicking anywhere in the
18827      * content area would normally start the drag operation.  Use this method
18828      * to specify that an element inside of the content div is the element
18829      * that starts the drag operation.
18830      * @method setHandleElId
18831      * @param id {string} the id of the element that will be used to
18832      * initiate the drag.
18833      */
18834     setHandleElId: function(id) {
18835         if (typeof id !== "string") {
18836             id = Roo.id(id);
18837         }
18838         this.handleElId = id;
18839         this.DDM.regHandle(this.id, id);
18840     },
18841
18842     /**
18843      * Allows you to set an element outside of the linked element as a drag
18844      * handle
18845      * @method setOuterHandleElId
18846      * @param id the id of the element that will be used to initiate the drag
18847      */
18848     setOuterHandleElId: function(id) {
18849         if (typeof id !== "string") {
18850             id = Roo.id(id);
18851         }
18852         Event.on(id, "mousedown",
18853                 this.handleMouseDown, this);
18854         this.setHandleElId(id);
18855
18856         this.hasOuterHandles = true;
18857     },
18858
18859     /**
18860      * Remove all drag and drop hooks for this element
18861      * @method unreg
18862      */
18863     unreg: function() {
18864         Event.un(this.id, "mousedown",
18865                 this.handleMouseDown);
18866         Event.un(this.id, "touchstart",
18867                 this.handleMouseDown);
18868         this._domRef = null;
18869         this.DDM._remove(this);
18870     },
18871
18872     destroy : function(){
18873         this.unreg();
18874     },
18875
18876     /**
18877      * Returns true if this instance is locked, or the drag drop mgr is locked
18878      * (meaning that all drag/drop is disabled on the page.)
18879      * @method isLocked
18880      * @return {boolean} true if this obj or all drag/drop is locked, else
18881      * false
18882      */
18883     isLocked: function() {
18884         return (this.DDM.isLocked() || this.locked);
18885     },
18886
18887     /**
18888      * Fired when this object is clicked
18889      * @method handleMouseDown
18890      * @param {Event} e
18891      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18892      * @private
18893      */
18894     handleMouseDown: function(e, oDD){
18895      
18896         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18897             //Roo.log('not touch/ button !=0');
18898             return;
18899         }
18900         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18901             return; // double touch..
18902         }
18903         
18904
18905         if (this.isLocked()) {
18906             //Roo.log('locked');
18907             return;
18908         }
18909
18910         this.DDM.refreshCache(this.groups);
18911 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18912         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18913         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18914             //Roo.log('no outer handes or not over target');
18915                 // do nothing.
18916         } else {
18917 //            Roo.log('check validator');
18918             if (this.clickValidator(e)) {
18919 //                Roo.log('validate success');
18920                 // set the initial element position
18921                 this.setStartPosition();
18922
18923
18924                 this.b4MouseDown(e);
18925                 this.onMouseDown(e);
18926
18927                 this.DDM.handleMouseDown(e, this);
18928
18929                 this.DDM.stopEvent(e);
18930             } else {
18931
18932
18933             }
18934         }
18935     },
18936
18937     clickValidator: function(e) {
18938         var target = e.getTarget();
18939         return ( this.isValidHandleChild(target) &&
18940                     (this.id == this.handleElId ||
18941                         this.DDM.handleWasClicked(target, this.id)) );
18942     },
18943
18944     /**
18945      * Allows you to specify a tag name that should not start a drag operation
18946      * when clicked.  This is designed to facilitate embedding links within a
18947      * drag handle that do something other than start the drag.
18948      * @method addInvalidHandleType
18949      * @param {string} tagName the type of element to exclude
18950      */
18951     addInvalidHandleType: function(tagName) {
18952         var type = tagName.toUpperCase();
18953         this.invalidHandleTypes[type] = type;
18954     },
18955
18956     /**
18957      * Lets you to specify an element id for a child of a drag handle
18958      * that should not initiate a drag
18959      * @method addInvalidHandleId
18960      * @param {string} id the element id of the element you wish to ignore
18961      */
18962     addInvalidHandleId: function(id) {
18963         if (typeof id !== "string") {
18964             id = Roo.id(id);
18965         }
18966         this.invalidHandleIds[id] = id;
18967     },
18968
18969     /**
18970      * Lets you specify a css class of elements that will not initiate a drag
18971      * @method addInvalidHandleClass
18972      * @param {string} cssClass the class of the elements you wish to ignore
18973      */
18974     addInvalidHandleClass: function(cssClass) {
18975         this.invalidHandleClasses.push(cssClass);
18976     },
18977
18978     /**
18979      * Unsets an excluded tag name set by addInvalidHandleType
18980      * @method removeInvalidHandleType
18981      * @param {string} tagName the type of element to unexclude
18982      */
18983     removeInvalidHandleType: function(tagName) {
18984         var type = tagName.toUpperCase();
18985         // this.invalidHandleTypes[type] = null;
18986         delete this.invalidHandleTypes[type];
18987     },
18988
18989     /**
18990      * Unsets an invalid handle id
18991      * @method removeInvalidHandleId
18992      * @param {string} id the id of the element to re-enable
18993      */
18994     removeInvalidHandleId: function(id) {
18995         if (typeof id !== "string") {
18996             id = Roo.id(id);
18997         }
18998         delete this.invalidHandleIds[id];
18999     },
19000
19001     /**
19002      * Unsets an invalid css class
19003      * @method removeInvalidHandleClass
19004      * @param {string} cssClass the class of the element(s) you wish to
19005      * re-enable
19006      */
19007     removeInvalidHandleClass: function(cssClass) {
19008         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19009             if (this.invalidHandleClasses[i] == cssClass) {
19010                 delete this.invalidHandleClasses[i];
19011             }
19012         }
19013     },
19014
19015     /**
19016      * Checks the tag exclusion list to see if this click should be ignored
19017      * @method isValidHandleChild
19018      * @param {HTMLElement} node the HTMLElement to evaluate
19019      * @return {boolean} true if this is a valid tag type, false if not
19020      */
19021     isValidHandleChild: function(node) {
19022
19023         var valid = true;
19024         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19025         var nodeName;
19026         try {
19027             nodeName = node.nodeName.toUpperCase();
19028         } catch(e) {
19029             nodeName = node.nodeName;
19030         }
19031         valid = valid && !this.invalidHandleTypes[nodeName];
19032         valid = valid && !this.invalidHandleIds[node.id];
19033
19034         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19035             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19036         }
19037
19038
19039         return valid;
19040
19041     },
19042
19043     /**
19044      * Create the array of horizontal tick marks if an interval was specified
19045      * in setXConstraint().
19046      * @method setXTicks
19047      * @private
19048      */
19049     setXTicks: function(iStartX, iTickSize) {
19050         this.xTicks = [];
19051         this.xTickSize = iTickSize;
19052
19053         var tickMap = {};
19054
19055         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19056             if (!tickMap[i]) {
19057                 this.xTicks[this.xTicks.length] = i;
19058                 tickMap[i] = true;
19059             }
19060         }
19061
19062         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19063             if (!tickMap[i]) {
19064                 this.xTicks[this.xTicks.length] = i;
19065                 tickMap[i] = true;
19066             }
19067         }
19068
19069         this.xTicks.sort(this.DDM.numericSort) ;
19070     },
19071
19072     /**
19073      * Create the array of vertical tick marks if an interval was specified in
19074      * setYConstraint().
19075      * @method setYTicks
19076      * @private
19077      */
19078     setYTicks: function(iStartY, iTickSize) {
19079         this.yTicks = [];
19080         this.yTickSize = iTickSize;
19081
19082         var tickMap = {};
19083
19084         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19085             if (!tickMap[i]) {
19086                 this.yTicks[this.yTicks.length] = i;
19087                 tickMap[i] = true;
19088             }
19089         }
19090
19091         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19092             if (!tickMap[i]) {
19093                 this.yTicks[this.yTicks.length] = i;
19094                 tickMap[i] = true;
19095             }
19096         }
19097
19098         this.yTicks.sort(this.DDM.numericSort) ;
19099     },
19100
19101     /**
19102      * By default, the element can be dragged any place on the screen.  Use
19103      * this method to limit the horizontal travel of the element.  Pass in
19104      * 0,0 for the parameters if you want to lock the drag to the y axis.
19105      * @method setXConstraint
19106      * @param {int} iLeft the number of pixels the element can move to the left
19107      * @param {int} iRight the number of pixels the element can move to the
19108      * right
19109      * @param {int} iTickSize optional parameter for specifying that the
19110      * element
19111      * should move iTickSize pixels at a time.
19112      */
19113     setXConstraint: function(iLeft, iRight, iTickSize) {
19114         this.leftConstraint = iLeft;
19115         this.rightConstraint = iRight;
19116
19117         this.minX = this.initPageX - iLeft;
19118         this.maxX = this.initPageX + iRight;
19119         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19120
19121         this.constrainX = true;
19122     },
19123
19124     /**
19125      * Clears any constraints applied to this instance.  Also clears ticks
19126      * since they can't exist independent of a constraint at this time.
19127      * @method clearConstraints
19128      */
19129     clearConstraints: function() {
19130         this.constrainX = false;
19131         this.constrainY = false;
19132         this.clearTicks();
19133     },
19134
19135     /**
19136      * Clears any tick interval defined for this instance
19137      * @method clearTicks
19138      */
19139     clearTicks: function() {
19140         this.xTicks = null;
19141         this.yTicks = null;
19142         this.xTickSize = 0;
19143         this.yTickSize = 0;
19144     },
19145
19146     /**
19147      * By default, the element can be dragged any place on the screen.  Set
19148      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19149      * parameters if you want to lock the drag to the x axis.
19150      * @method setYConstraint
19151      * @param {int} iUp the number of pixels the element can move up
19152      * @param {int} iDown the number of pixels the element can move down
19153      * @param {int} iTickSize optional parameter for specifying that the
19154      * element should move iTickSize pixels at a time.
19155      */
19156     setYConstraint: function(iUp, iDown, iTickSize) {
19157         this.topConstraint = iUp;
19158         this.bottomConstraint = iDown;
19159
19160         this.minY = this.initPageY - iUp;
19161         this.maxY = this.initPageY + iDown;
19162         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19163
19164         this.constrainY = true;
19165
19166     },
19167
19168     /**
19169      * resetConstraints must be called if you manually reposition a dd element.
19170      * @method resetConstraints
19171      * @param {boolean} maintainOffset
19172      */
19173     resetConstraints: function() {
19174
19175
19176         // Maintain offsets if necessary
19177         if (this.initPageX || this.initPageX === 0) {
19178             // figure out how much this thing has moved
19179             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19180             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19181
19182             this.setInitPosition(dx, dy);
19183
19184         // This is the first time we have detected the element's position
19185         } else {
19186             this.setInitPosition();
19187         }
19188
19189         if (this.constrainX) {
19190             this.setXConstraint( this.leftConstraint,
19191                                  this.rightConstraint,
19192                                  this.xTickSize        );
19193         }
19194
19195         if (this.constrainY) {
19196             this.setYConstraint( this.topConstraint,
19197                                  this.bottomConstraint,
19198                                  this.yTickSize         );
19199         }
19200     },
19201
19202     /**
19203      * Normally the drag element is moved pixel by pixel, but we can specify
19204      * that it move a number of pixels at a time.  This method resolves the
19205      * location when we have it set up like this.
19206      * @method getTick
19207      * @param {int} val where we want to place the object
19208      * @param {int[]} tickArray sorted array of valid points
19209      * @return {int} the closest tick
19210      * @private
19211      */
19212     getTick: function(val, tickArray) {
19213
19214         if (!tickArray) {
19215             // If tick interval is not defined, it is effectively 1 pixel,
19216             // so we return the value passed to us.
19217             return val;
19218         } else if (tickArray[0] >= val) {
19219             // The value is lower than the first tick, so we return the first
19220             // tick.
19221             return tickArray[0];
19222         } else {
19223             for (var i=0, len=tickArray.length; i<len; ++i) {
19224                 var next = i + 1;
19225                 if (tickArray[next] && tickArray[next] >= val) {
19226                     var diff1 = val - tickArray[i];
19227                     var diff2 = tickArray[next] - val;
19228                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19229                 }
19230             }
19231
19232             // The value is larger than the last tick, so we return the last
19233             // tick.
19234             return tickArray[tickArray.length - 1];
19235         }
19236     },
19237
19238     /**
19239      * toString method
19240      * @method toString
19241      * @return {string} string representation of the dd obj
19242      */
19243     toString: function() {
19244         return ("DragDrop " + this.id);
19245     }
19246
19247 });
19248
19249 })();
19250 /*
19251  * Based on:
19252  * Ext JS Library 1.1.1
19253  * Copyright(c) 2006-2007, Ext JS, LLC.
19254  *
19255  * Originally Released Under LGPL - original licence link has changed is not relivant.
19256  *
19257  * Fork - LGPL
19258  * <script type="text/javascript">
19259  */
19260
19261
19262 /**
19263  * The drag and drop utility provides a framework for building drag and drop
19264  * applications.  In addition to enabling drag and drop for specific elements,
19265  * the drag and drop elements are tracked by the manager class, and the
19266  * interactions between the various elements are tracked during the drag and
19267  * the implementing code is notified about these important moments.
19268  */
19269
19270 // Only load the library once.  Rewriting the manager class would orphan
19271 // existing drag and drop instances.
19272 if (!Roo.dd.DragDropMgr) {
19273
19274 /**
19275  * @class Roo.dd.DragDropMgr
19276  * DragDropMgr is a singleton that tracks the element interaction for
19277  * all DragDrop items in the window.  Generally, you will not call
19278  * this class directly, but it does have helper methods that could
19279  * be useful in your DragDrop implementations.
19280  * @singleton
19281  */
19282 Roo.dd.DragDropMgr = function() {
19283
19284     var Event = Roo.EventManager;
19285
19286     return {
19287
19288         /**
19289          * Two dimensional Array of registered DragDrop objects.  The first
19290          * dimension is the DragDrop item group, the second the DragDrop
19291          * object.
19292          * @property ids
19293          * @type {string: string}
19294          * @private
19295          * @static
19296          */
19297         ids: {},
19298
19299         /**
19300          * Array of element ids defined as drag handles.  Used to determine
19301          * if the element that generated the mousedown event is actually the
19302          * handle and not the html element itself.
19303          * @property handleIds
19304          * @type {string: string}
19305          * @private
19306          * @static
19307          */
19308         handleIds: {},
19309
19310         /**
19311          * the DragDrop object that is currently being dragged
19312          * @property dragCurrent
19313          * @type DragDrop
19314          * @private
19315          * @static
19316          **/
19317         dragCurrent: null,
19318
19319         /**
19320          * the DragDrop object(s) that are being hovered over
19321          * @property dragOvers
19322          * @type Array
19323          * @private
19324          * @static
19325          */
19326         dragOvers: {},
19327
19328         /**
19329          * the X distance between the cursor and the object being dragged
19330          * @property deltaX
19331          * @type int
19332          * @private
19333          * @static
19334          */
19335         deltaX: 0,
19336
19337         /**
19338          * the Y distance between the cursor and the object being dragged
19339          * @property deltaY
19340          * @type int
19341          * @private
19342          * @static
19343          */
19344         deltaY: 0,
19345
19346         /**
19347          * Flag to determine if we should prevent the default behavior of the
19348          * events we define. By default this is true, but this can be set to
19349          * false if you need the default behavior (not recommended)
19350          * @property preventDefault
19351          * @type boolean
19352          * @static
19353          */
19354         preventDefault: true,
19355
19356         /**
19357          * Flag to determine if we should stop the propagation of the events
19358          * we generate. This is true by default but you may want to set it to
19359          * false if the html element contains other features that require the
19360          * mouse click.
19361          * @property stopPropagation
19362          * @type boolean
19363          * @static
19364          */
19365         stopPropagation: true,
19366
19367         /**
19368          * Internal flag that is set to true when drag and drop has been
19369          * intialized
19370          * @property initialized
19371          * @private
19372          * @static
19373          */
19374         initalized: false,
19375
19376         /**
19377          * All drag and drop can be disabled.
19378          * @property locked
19379          * @private
19380          * @static
19381          */
19382         locked: false,
19383
19384         /**
19385          * Called the first time an element is registered.
19386          * @method init
19387          * @private
19388          * @static
19389          */
19390         init: function() {
19391             this.initialized = true;
19392         },
19393
19394         /**
19395          * In point mode, drag and drop interaction is defined by the
19396          * location of the cursor during the drag/drop
19397          * @property POINT
19398          * @type int
19399          * @static
19400          */
19401         POINT: 0,
19402
19403         /**
19404          * In intersect mode, drag and drop interactio nis defined by the
19405          * overlap of two or more drag and drop objects.
19406          * @property INTERSECT
19407          * @type int
19408          * @static
19409          */
19410         INTERSECT: 1,
19411
19412         /**
19413          * The current drag and drop mode.  Default: POINT
19414          * @property mode
19415          * @type int
19416          * @static
19417          */
19418         mode: 0,
19419
19420         /**
19421          * Runs method on all drag and drop objects
19422          * @method _execOnAll
19423          * @private
19424          * @static
19425          */
19426         _execOnAll: function(sMethod, args) {
19427             for (var i in this.ids) {
19428                 for (var j in this.ids[i]) {
19429                     var oDD = this.ids[i][j];
19430                     if (! this.isTypeOfDD(oDD)) {
19431                         continue;
19432                     }
19433                     oDD[sMethod].apply(oDD, args);
19434                 }
19435             }
19436         },
19437
19438         /**
19439          * Drag and drop initialization.  Sets up the global event handlers
19440          * @method _onLoad
19441          * @private
19442          * @static
19443          */
19444         _onLoad: function() {
19445
19446             this.init();
19447
19448             if (!Roo.isTouch) {
19449                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19450                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19451             }
19452             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19453             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19454             
19455             Event.on(window,   "unload",    this._onUnload, this, true);
19456             Event.on(window,   "resize",    this._onResize, this, true);
19457             // Event.on(window,   "mouseout",    this._test);
19458
19459         },
19460
19461         /**
19462          * Reset constraints on all drag and drop objs
19463          * @method _onResize
19464          * @private
19465          * @static
19466          */
19467         _onResize: function(e) {
19468             this._execOnAll("resetConstraints", []);
19469         },
19470
19471         /**
19472          * Lock all drag and drop functionality
19473          * @method lock
19474          * @static
19475          */
19476         lock: function() { this.locked = true; },
19477
19478         /**
19479          * Unlock all drag and drop functionality
19480          * @method unlock
19481          * @static
19482          */
19483         unlock: function() { this.locked = false; },
19484
19485         /**
19486          * Is drag and drop locked?
19487          * @method isLocked
19488          * @return {boolean} True if drag and drop is locked, false otherwise.
19489          * @static
19490          */
19491         isLocked: function() { return this.locked; },
19492
19493         /**
19494          * Location cache that is set for all drag drop objects when a drag is
19495          * initiated, cleared when the drag is finished.
19496          * @property locationCache
19497          * @private
19498          * @static
19499          */
19500         locationCache: {},
19501
19502         /**
19503          * Set useCache to false if you want to force object the lookup of each
19504          * drag and drop linked element constantly during a drag.
19505          * @property useCache
19506          * @type boolean
19507          * @static
19508          */
19509         useCache: true,
19510
19511         /**
19512          * The number of pixels that the mouse needs to move after the
19513          * mousedown before the drag is initiated.  Default=3;
19514          * @property clickPixelThresh
19515          * @type int
19516          * @static
19517          */
19518         clickPixelThresh: 3,
19519
19520         /**
19521          * The number of milliseconds after the mousedown event to initiate the
19522          * drag if we don't get a mouseup event. Default=1000
19523          * @property clickTimeThresh
19524          * @type int
19525          * @static
19526          */
19527         clickTimeThresh: 350,
19528
19529         /**
19530          * Flag that indicates that either the drag pixel threshold or the
19531          * mousdown time threshold has been met
19532          * @property dragThreshMet
19533          * @type boolean
19534          * @private
19535          * @static
19536          */
19537         dragThreshMet: false,
19538
19539         /**
19540          * Timeout used for the click time threshold
19541          * @property clickTimeout
19542          * @type Object
19543          * @private
19544          * @static
19545          */
19546         clickTimeout: null,
19547
19548         /**
19549          * The X position of the mousedown event stored for later use when a
19550          * drag threshold is met.
19551          * @property startX
19552          * @type int
19553          * @private
19554          * @static
19555          */
19556         startX: 0,
19557
19558         /**
19559          * The Y position of the mousedown event stored for later use when a
19560          * drag threshold is met.
19561          * @property startY
19562          * @type int
19563          * @private
19564          * @static
19565          */
19566         startY: 0,
19567
19568         /**
19569          * Each DragDrop instance must be registered with the DragDropMgr.
19570          * This is executed in DragDrop.init()
19571          * @method regDragDrop
19572          * @param {DragDrop} oDD the DragDrop object to register
19573          * @param {String} sGroup the name of the group this element belongs to
19574          * @static
19575          */
19576         regDragDrop: function(oDD, sGroup) {
19577             if (!this.initialized) { this.init(); }
19578
19579             if (!this.ids[sGroup]) {
19580                 this.ids[sGroup] = {};
19581             }
19582             this.ids[sGroup][oDD.id] = oDD;
19583         },
19584
19585         /**
19586          * Removes the supplied dd instance from the supplied group. Executed
19587          * by DragDrop.removeFromGroup, so don't call this function directly.
19588          * @method removeDDFromGroup
19589          * @private
19590          * @static
19591          */
19592         removeDDFromGroup: function(oDD, sGroup) {
19593             if (!this.ids[sGroup]) {
19594                 this.ids[sGroup] = {};
19595             }
19596
19597             var obj = this.ids[sGroup];
19598             if (obj && obj[oDD.id]) {
19599                 delete obj[oDD.id];
19600             }
19601         },
19602
19603         /**
19604          * Unregisters a drag and drop item.  This is executed in
19605          * DragDrop.unreg, use that method instead of calling this directly.
19606          * @method _remove
19607          * @private
19608          * @static
19609          */
19610         _remove: function(oDD) {
19611             for (var g in oDD.groups) {
19612                 if (g && this.ids[g][oDD.id]) {
19613                     delete this.ids[g][oDD.id];
19614                 }
19615             }
19616             delete this.handleIds[oDD.id];
19617         },
19618
19619         /**
19620          * Each DragDrop handle element must be registered.  This is done
19621          * automatically when executing DragDrop.setHandleElId()
19622          * @method regHandle
19623          * @param {String} sDDId the DragDrop id this element is a handle for
19624          * @param {String} sHandleId the id of the element that is the drag
19625          * handle
19626          * @static
19627          */
19628         regHandle: function(sDDId, sHandleId) {
19629             if (!this.handleIds[sDDId]) {
19630                 this.handleIds[sDDId] = {};
19631             }
19632             this.handleIds[sDDId][sHandleId] = sHandleId;
19633         },
19634
19635         /**
19636          * Utility function to determine if a given element has been
19637          * registered as a drag drop item.
19638          * @method isDragDrop
19639          * @param {String} id the element id to check
19640          * @return {boolean} true if this element is a DragDrop item,
19641          * false otherwise
19642          * @static
19643          */
19644         isDragDrop: function(id) {
19645             return ( this.getDDById(id) ) ? true : false;
19646         },
19647
19648         /**
19649          * Returns the drag and drop instances that are in all groups the
19650          * passed in instance belongs to.
19651          * @method getRelated
19652          * @param {DragDrop} p_oDD the obj to get related data for
19653          * @param {boolean} bTargetsOnly if true, only return targetable objs
19654          * @return {DragDrop[]} the related instances
19655          * @static
19656          */
19657         getRelated: function(p_oDD, bTargetsOnly) {
19658             var oDDs = [];
19659             for (var i in p_oDD.groups) {
19660                 for (j in this.ids[i]) {
19661                     var dd = this.ids[i][j];
19662                     if (! this.isTypeOfDD(dd)) {
19663                         continue;
19664                     }
19665                     if (!bTargetsOnly || dd.isTarget) {
19666                         oDDs[oDDs.length] = dd;
19667                     }
19668                 }
19669             }
19670
19671             return oDDs;
19672         },
19673
19674         /**
19675          * Returns true if the specified dd target is a legal target for
19676          * the specifice drag obj
19677          * @method isLegalTarget
19678          * @param {DragDrop} the drag obj
19679          * @param {DragDrop} the target
19680          * @return {boolean} true if the target is a legal target for the
19681          * dd obj
19682          * @static
19683          */
19684         isLegalTarget: function (oDD, oTargetDD) {
19685             var targets = this.getRelated(oDD, true);
19686             for (var i=0, len=targets.length;i<len;++i) {
19687                 if (targets[i].id == oTargetDD.id) {
19688                     return true;
19689                 }
19690             }
19691
19692             return false;
19693         },
19694
19695         /**
19696          * My goal is to be able to transparently determine if an object is
19697          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19698          * returns "object", oDD.constructor.toString() always returns
19699          * "DragDrop" and not the name of the subclass.  So for now it just
19700          * evaluates a well-known variable in DragDrop.
19701          * @method isTypeOfDD
19702          * @param {Object} the object to evaluate
19703          * @return {boolean} true if typeof oDD = DragDrop
19704          * @static
19705          */
19706         isTypeOfDD: function (oDD) {
19707             return (oDD && oDD.__ygDragDrop);
19708         },
19709
19710         /**
19711          * Utility function to determine if a given element has been
19712          * registered as a drag drop handle for the given Drag Drop object.
19713          * @method isHandle
19714          * @param {String} id the element id to check
19715          * @return {boolean} true if this element is a DragDrop handle, false
19716          * otherwise
19717          * @static
19718          */
19719         isHandle: function(sDDId, sHandleId) {
19720             return ( this.handleIds[sDDId] &&
19721                             this.handleIds[sDDId][sHandleId] );
19722         },
19723
19724         /**
19725          * Returns the DragDrop instance for a given id
19726          * @method getDDById
19727          * @param {String} id the id of the DragDrop object
19728          * @return {DragDrop} the drag drop object, null if it is not found
19729          * @static
19730          */
19731         getDDById: function(id) {
19732             for (var i in this.ids) {
19733                 if (this.ids[i][id]) {
19734                     return this.ids[i][id];
19735                 }
19736             }
19737             return null;
19738         },
19739
19740         /**
19741          * Fired after a registered DragDrop object gets the mousedown event.
19742          * Sets up the events required to track the object being dragged
19743          * @method handleMouseDown
19744          * @param {Event} e the event
19745          * @param oDD the DragDrop object being dragged
19746          * @private
19747          * @static
19748          */
19749         handleMouseDown: function(e, oDD) {
19750             if(Roo.QuickTips){
19751                 Roo.QuickTips.disable();
19752             }
19753             this.currentTarget = e.getTarget();
19754
19755             this.dragCurrent = oDD;
19756
19757             var el = oDD.getEl();
19758
19759             // track start position
19760             this.startX = e.getPageX();
19761             this.startY = e.getPageY();
19762
19763             this.deltaX = this.startX - el.offsetLeft;
19764             this.deltaY = this.startY - el.offsetTop;
19765
19766             this.dragThreshMet = false;
19767
19768             this.clickTimeout = setTimeout(
19769                     function() {
19770                         var DDM = Roo.dd.DDM;
19771                         DDM.startDrag(DDM.startX, DDM.startY);
19772                     },
19773                     this.clickTimeThresh );
19774         },
19775
19776         /**
19777          * Fired when either the drag pixel threshol or the mousedown hold
19778          * time threshold has been met.
19779          * @method startDrag
19780          * @param x {int} the X position of the original mousedown
19781          * @param y {int} the Y position of the original mousedown
19782          * @static
19783          */
19784         startDrag: function(x, y) {
19785             clearTimeout(this.clickTimeout);
19786             if (this.dragCurrent) {
19787                 this.dragCurrent.b4StartDrag(x, y);
19788                 this.dragCurrent.startDrag(x, y);
19789             }
19790             this.dragThreshMet = true;
19791         },
19792
19793         /**
19794          * Internal function to handle the mouseup event.  Will be invoked
19795          * from the context of the document.
19796          * @method handleMouseUp
19797          * @param {Event} e the event
19798          * @private
19799          * @static
19800          */
19801         handleMouseUp: function(e) {
19802
19803             if(Roo.QuickTips){
19804                 Roo.QuickTips.enable();
19805             }
19806             if (! this.dragCurrent) {
19807                 return;
19808             }
19809
19810             clearTimeout(this.clickTimeout);
19811
19812             if (this.dragThreshMet) {
19813                 this.fireEvents(e, true);
19814             } else {
19815             }
19816
19817             this.stopDrag(e);
19818
19819             this.stopEvent(e);
19820         },
19821
19822         /**
19823          * Utility to stop event propagation and event default, if these
19824          * features are turned on.
19825          * @method stopEvent
19826          * @param {Event} e the event as returned by this.getEvent()
19827          * @static
19828          */
19829         stopEvent: function(e){
19830             if(this.stopPropagation) {
19831                 e.stopPropagation();
19832             }
19833
19834             if (this.preventDefault) {
19835                 e.preventDefault();
19836             }
19837         },
19838
19839         /**
19840          * Internal function to clean up event handlers after the drag
19841          * operation is complete
19842          * @method stopDrag
19843          * @param {Event} e the event
19844          * @private
19845          * @static
19846          */
19847         stopDrag: function(e) {
19848             // Fire the drag end event for the item that was dragged
19849             if (this.dragCurrent) {
19850                 if (this.dragThreshMet) {
19851                     this.dragCurrent.b4EndDrag(e);
19852                     this.dragCurrent.endDrag(e);
19853                 }
19854
19855                 this.dragCurrent.onMouseUp(e);
19856             }
19857
19858             this.dragCurrent = null;
19859             this.dragOvers = {};
19860         },
19861
19862         /**
19863          * Internal function to handle the mousemove event.  Will be invoked
19864          * from the context of the html element.
19865          *
19866          * @TODO figure out what we can do about mouse events lost when the
19867          * user drags objects beyond the window boundary.  Currently we can
19868          * detect this in internet explorer by verifying that the mouse is
19869          * down during the mousemove event.  Firefox doesn't give us the
19870          * button state on the mousemove event.
19871          * @method handleMouseMove
19872          * @param {Event} e the event
19873          * @private
19874          * @static
19875          */
19876         handleMouseMove: function(e) {
19877             if (! this.dragCurrent) {
19878                 return true;
19879             }
19880
19881             // var button = e.which || e.button;
19882
19883             // check for IE mouseup outside of page boundary
19884             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19885                 this.stopEvent(e);
19886                 return this.handleMouseUp(e);
19887             }
19888
19889             if (!this.dragThreshMet) {
19890                 var diffX = Math.abs(this.startX - e.getPageX());
19891                 var diffY = Math.abs(this.startY - e.getPageY());
19892                 if (diffX > this.clickPixelThresh ||
19893                             diffY > this.clickPixelThresh) {
19894                     this.startDrag(this.startX, this.startY);
19895                 }
19896             }
19897
19898             if (this.dragThreshMet) {
19899                 this.dragCurrent.b4Drag(e);
19900                 this.dragCurrent.onDrag(e);
19901                 if(!this.dragCurrent.moveOnly){
19902                     this.fireEvents(e, false);
19903                 }
19904             }
19905
19906             this.stopEvent(e);
19907
19908             return true;
19909         },
19910
19911         /**
19912          * Iterates over all of the DragDrop elements to find ones we are
19913          * hovering over or dropping on
19914          * @method fireEvents
19915          * @param {Event} e the event
19916          * @param {boolean} isDrop is this a drop op or a mouseover op?
19917          * @private
19918          * @static
19919          */
19920         fireEvents: function(e, isDrop) {
19921             var dc = this.dragCurrent;
19922
19923             // If the user did the mouse up outside of the window, we could
19924             // get here even though we have ended the drag.
19925             if (!dc || dc.isLocked()) {
19926                 return;
19927             }
19928
19929             var pt = e.getPoint();
19930
19931             // cache the previous dragOver array
19932             var oldOvers = [];
19933
19934             var outEvts   = [];
19935             var overEvts  = [];
19936             var dropEvts  = [];
19937             var enterEvts = [];
19938
19939             // Check to see if the object(s) we were hovering over is no longer
19940             // being hovered over so we can fire the onDragOut event
19941             for (var i in this.dragOvers) {
19942
19943                 var ddo = this.dragOvers[i];
19944
19945                 if (! this.isTypeOfDD(ddo)) {
19946                     continue;
19947                 }
19948
19949                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19950                     outEvts.push( ddo );
19951                 }
19952
19953                 oldOvers[i] = true;
19954                 delete this.dragOvers[i];
19955             }
19956
19957             for (var sGroup in dc.groups) {
19958
19959                 if ("string" != typeof sGroup) {
19960                     continue;
19961                 }
19962
19963                 for (i in this.ids[sGroup]) {
19964                     var oDD = this.ids[sGroup][i];
19965                     if (! this.isTypeOfDD(oDD)) {
19966                         continue;
19967                     }
19968
19969                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19970                         if (this.isOverTarget(pt, oDD, this.mode)) {
19971                             // look for drop interactions
19972                             if (isDrop) {
19973                                 dropEvts.push( oDD );
19974                             // look for drag enter and drag over interactions
19975                             } else {
19976
19977                                 // initial drag over: dragEnter fires
19978                                 if (!oldOvers[oDD.id]) {
19979                                     enterEvts.push( oDD );
19980                                 // subsequent drag overs: dragOver fires
19981                                 } else {
19982                                     overEvts.push( oDD );
19983                                 }
19984
19985                                 this.dragOvers[oDD.id] = oDD;
19986                             }
19987                         }
19988                     }
19989                 }
19990             }
19991
19992             if (this.mode) {
19993                 if (outEvts.length) {
19994                     dc.b4DragOut(e, outEvts);
19995                     dc.onDragOut(e, outEvts);
19996                 }
19997
19998                 if (enterEvts.length) {
19999                     dc.onDragEnter(e, enterEvts);
20000                 }
20001
20002                 if (overEvts.length) {
20003                     dc.b4DragOver(e, overEvts);
20004                     dc.onDragOver(e, overEvts);
20005                 }
20006
20007                 if (dropEvts.length) {
20008                     dc.b4DragDrop(e, dropEvts);
20009                     dc.onDragDrop(e, dropEvts);
20010                 }
20011
20012             } else {
20013                 // fire dragout events
20014                 var len = 0;
20015                 for (i=0, len=outEvts.length; i<len; ++i) {
20016                     dc.b4DragOut(e, outEvts[i].id);
20017                     dc.onDragOut(e, outEvts[i].id);
20018                 }
20019
20020                 // fire enter events
20021                 for (i=0,len=enterEvts.length; i<len; ++i) {
20022                     // dc.b4DragEnter(e, oDD.id);
20023                     dc.onDragEnter(e, enterEvts[i].id);
20024                 }
20025
20026                 // fire over events
20027                 for (i=0,len=overEvts.length; i<len; ++i) {
20028                     dc.b4DragOver(e, overEvts[i].id);
20029                     dc.onDragOver(e, overEvts[i].id);
20030                 }
20031
20032                 // fire drop events
20033                 for (i=0, len=dropEvts.length; i<len; ++i) {
20034                     dc.b4DragDrop(e, dropEvts[i].id);
20035                     dc.onDragDrop(e, dropEvts[i].id);
20036                 }
20037
20038             }
20039
20040             // notify about a drop that did not find a target
20041             if (isDrop && !dropEvts.length) {
20042                 dc.onInvalidDrop(e);
20043             }
20044
20045         },
20046
20047         /**
20048          * Helper function for getting the best match from the list of drag
20049          * and drop objects returned by the drag and drop events when we are
20050          * in INTERSECT mode.  It returns either the first object that the
20051          * cursor is over, or the object that has the greatest overlap with
20052          * the dragged element.
20053          * @method getBestMatch
20054          * @param  {DragDrop[]} dds The array of drag and drop objects
20055          * targeted
20056          * @return {DragDrop}       The best single match
20057          * @static
20058          */
20059         getBestMatch: function(dds) {
20060             var winner = null;
20061             // Return null if the input is not what we expect
20062             //if (!dds || !dds.length || dds.length == 0) {
20063                // winner = null;
20064             // If there is only one item, it wins
20065             //} else if (dds.length == 1) {
20066
20067             var len = dds.length;
20068
20069             if (len == 1) {
20070                 winner = dds[0];
20071             } else {
20072                 // Loop through the targeted items
20073                 for (var i=0; i<len; ++i) {
20074                     var dd = dds[i];
20075                     // If the cursor is over the object, it wins.  If the
20076                     // cursor is over multiple matches, the first one we come
20077                     // to wins.
20078                     if (dd.cursorIsOver) {
20079                         winner = dd;
20080                         break;
20081                     // Otherwise the object with the most overlap wins
20082                     } else {
20083                         if (!winner ||
20084                             winner.overlap.getArea() < dd.overlap.getArea()) {
20085                             winner = dd;
20086                         }
20087                     }
20088                 }
20089             }
20090
20091             return winner;
20092         },
20093
20094         /**
20095          * Refreshes the cache of the top-left and bottom-right points of the
20096          * drag and drop objects in the specified group(s).  This is in the
20097          * format that is stored in the drag and drop instance, so typical
20098          * usage is:
20099          * <code>
20100          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20101          * </code>
20102          * Alternatively:
20103          * <code>
20104          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20105          * </code>
20106          * @TODO this really should be an indexed array.  Alternatively this
20107          * method could accept both.
20108          * @method refreshCache
20109          * @param {Object} groups an associative array of groups to refresh
20110          * @static
20111          */
20112         refreshCache: function(groups) {
20113             for (var sGroup in groups) {
20114                 if ("string" != typeof sGroup) {
20115                     continue;
20116                 }
20117                 for (var i in this.ids[sGroup]) {
20118                     var oDD = this.ids[sGroup][i];
20119
20120                     if (this.isTypeOfDD(oDD)) {
20121                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20122                         var loc = this.getLocation(oDD);
20123                         if (loc) {
20124                             this.locationCache[oDD.id] = loc;
20125                         } else {
20126                             delete this.locationCache[oDD.id];
20127                             // this will unregister the drag and drop object if
20128                             // the element is not in a usable state
20129                             // oDD.unreg();
20130                         }
20131                     }
20132                 }
20133             }
20134         },
20135
20136         /**
20137          * This checks to make sure an element exists and is in the DOM.  The
20138          * main purpose is to handle cases where innerHTML is used to remove
20139          * drag and drop objects from the DOM.  IE provides an 'unspecified
20140          * error' when trying to access the offsetParent of such an element
20141          * @method verifyEl
20142          * @param {HTMLElement} el the element to check
20143          * @return {boolean} true if the element looks usable
20144          * @static
20145          */
20146         verifyEl: function(el) {
20147             if (el) {
20148                 var parent;
20149                 if(Roo.isIE){
20150                     try{
20151                         parent = el.offsetParent;
20152                     }catch(e){}
20153                 }else{
20154                     parent = el.offsetParent;
20155                 }
20156                 if (parent) {
20157                     return true;
20158                 }
20159             }
20160
20161             return false;
20162         },
20163
20164         /**
20165          * Returns a Region object containing the drag and drop element's position
20166          * and size, including the padding configured for it
20167          * @method getLocation
20168          * @param {DragDrop} oDD the drag and drop object to get the
20169          *                       location for
20170          * @return {Roo.lib.Region} a Region object representing the total area
20171          *                             the element occupies, including any padding
20172          *                             the instance is configured for.
20173          * @static
20174          */
20175         getLocation: function(oDD) {
20176             if (! this.isTypeOfDD(oDD)) {
20177                 return null;
20178             }
20179
20180             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20181
20182             try {
20183                 pos= Roo.lib.Dom.getXY(el);
20184             } catch (e) { }
20185
20186             if (!pos) {
20187                 return null;
20188             }
20189
20190             x1 = pos[0];
20191             x2 = x1 + el.offsetWidth;
20192             y1 = pos[1];
20193             y2 = y1 + el.offsetHeight;
20194
20195             t = y1 - oDD.padding[0];
20196             r = x2 + oDD.padding[1];
20197             b = y2 + oDD.padding[2];
20198             l = x1 - oDD.padding[3];
20199
20200             return new Roo.lib.Region( t, r, b, l );
20201         },
20202
20203         /**
20204          * Checks the cursor location to see if it over the target
20205          * @method isOverTarget
20206          * @param {Roo.lib.Point} pt The point to evaluate
20207          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20208          * @return {boolean} true if the mouse is over the target
20209          * @private
20210          * @static
20211          */
20212         isOverTarget: function(pt, oTarget, intersect) {
20213             // use cache if available
20214             var loc = this.locationCache[oTarget.id];
20215             if (!loc || !this.useCache) {
20216                 loc = this.getLocation(oTarget);
20217                 this.locationCache[oTarget.id] = loc;
20218
20219             }
20220
20221             if (!loc) {
20222                 return false;
20223             }
20224
20225             oTarget.cursorIsOver = loc.contains( pt );
20226
20227             // DragDrop is using this as a sanity check for the initial mousedown
20228             // in this case we are done.  In POINT mode, if the drag obj has no
20229             // contraints, we are also done. Otherwise we need to evaluate the
20230             // location of the target as related to the actual location of the
20231             // dragged element.
20232             var dc = this.dragCurrent;
20233             if (!dc || !dc.getTargetCoord ||
20234                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20235                 return oTarget.cursorIsOver;
20236             }
20237
20238             oTarget.overlap = null;
20239
20240             // Get the current location of the drag element, this is the
20241             // location of the mouse event less the delta that represents
20242             // where the original mousedown happened on the element.  We
20243             // need to consider constraints and ticks as well.
20244             var pos = dc.getTargetCoord(pt.x, pt.y);
20245
20246             var el = dc.getDragEl();
20247             var curRegion = new Roo.lib.Region( pos.y,
20248                                                    pos.x + el.offsetWidth,
20249                                                    pos.y + el.offsetHeight,
20250                                                    pos.x );
20251
20252             var overlap = curRegion.intersect(loc);
20253
20254             if (overlap) {
20255                 oTarget.overlap = overlap;
20256                 return (intersect) ? true : oTarget.cursorIsOver;
20257             } else {
20258                 return false;
20259             }
20260         },
20261
20262         /**
20263          * unload event handler
20264          * @method _onUnload
20265          * @private
20266          * @static
20267          */
20268         _onUnload: function(e, me) {
20269             Roo.dd.DragDropMgr.unregAll();
20270         },
20271
20272         /**
20273          * Cleans up the drag and drop events and objects.
20274          * @method unregAll
20275          * @private
20276          * @static
20277          */
20278         unregAll: function() {
20279
20280             if (this.dragCurrent) {
20281                 this.stopDrag();
20282                 this.dragCurrent = null;
20283             }
20284
20285             this._execOnAll("unreg", []);
20286
20287             for (i in this.elementCache) {
20288                 delete this.elementCache[i];
20289             }
20290
20291             this.elementCache = {};
20292             this.ids = {};
20293         },
20294
20295         /**
20296          * A cache of DOM elements
20297          * @property elementCache
20298          * @private
20299          * @static
20300          */
20301         elementCache: {},
20302
20303         /**
20304          * Get the wrapper for the DOM element specified
20305          * @method getElWrapper
20306          * @param {String} id the id of the element to get
20307          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20308          * @private
20309          * @deprecated This wrapper isn't that useful
20310          * @static
20311          */
20312         getElWrapper: function(id) {
20313             var oWrapper = this.elementCache[id];
20314             if (!oWrapper || !oWrapper.el) {
20315                 oWrapper = this.elementCache[id] =
20316                     new this.ElementWrapper(Roo.getDom(id));
20317             }
20318             return oWrapper;
20319         },
20320
20321         /**
20322          * Returns the actual DOM element
20323          * @method getElement
20324          * @param {String} id the id of the elment to get
20325          * @return {Object} The element
20326          * @deprecated use Roo.getDom instead
20327          * @static
20328          */
20329         getElement: function(id) {
20330             return Roo.getDom(id);
20331         },
20332
20333         /**
20334          * Returns the style property for the DOM element (i.e.,
20335          * document.getElById(id).style)
20336          * @method getCss
20337          * @param {String} id the id of the elment to get
20338          * @return {Object} The style property of the element
20339          * @deprecated use Roo.getDom instead
20340          * @static
20341          */
20342         getCss: function(id) {
20343             var el = Roo.getDom(id);
20344             return (el) ? el.style : null;
20345         },
20346
20347         /**
20348          * Inner class for cached elements
20349          * @class DragDropMgr.ElementWrapper
20350          * @for DragDropMgr
20351          * @private
20352          * @deprecated
20353          */
20354         ElementWrapper: function(el) {
20355                 /**
20356                  * The element
20357                  * @property el
20358                  */
20359                 this.el = el || null;
20360                 /**
20361                  * The element id
20362                  * @property id
20363                  */
20364                 this.id = this.el && el.id;
20365                 /**
20366                  * A reference to the style property
20367                  * @property css
20368                  */
20369                 this.css = this.el && el.style;
20370             },
20371
20372         /**
20373          * Returns the X position of an html element
20374          * @method getPosX
20375          * @param el the element for which to get the position
20376          * @return {int} the X coordinate
20377          * @for DragDropMgr
20378          * @deprecated use Roo.lib.Dom.getX instead
20379          * @static
20380          */
20381         getPosX: function(el) {
20382             return Roo.lib.Dom.getX(el);
20383         },
20384
20385         /**
20386          * Returns the Y position of an html element
20387          * @method getPosY
20388          * @param el the element for which to get the position
20389          * @return {int} the Y coordinate
20390          * @deprecated use Roo.lib.Dom.getY instead
20391          * @static
20392          */
20393         getPosY: function(el) {
20394             return Roo.lib.Dom.getY(el);
20395         },
20396
20397         /**
20398          * Swap two nodes.  In IE, we use the native method, for others we
20399          * emulate the IE behavior
20400          * @method swapNode
20401          * @param n1 the first node to swap
20402          * @param n2 the other node to swap
20403          * @static
20404          */
20405         swapNode: function(n1, n2) {
20406             if (n1.swapNode) {
20407                 n1.swapNode(n2);
20408             } else {
20409                 var p = n2.parentNode;
20410                 var s = n2.nextSibling;
20411
20412                 if (s == n1) {
20413                     p.insertBefore(n1, n2);
20414                 } else if (n2 == n1.nextSibling) {
20415                     p.insertBefore(n2, n1);
20416                 } else {
20417                     n1.parentNode.replaceChild(n2, n1);
20418                     p.insertBefore(n1, s);
20419                 }
20420             }
20421         },
20422
20423         /**
20424          * Returns the current scroll position
20425          * @method getScroll
20426          * @private
20427          * @static
20428          */
20429         getScroll: function () {
20430             var t, l, dde=document.documentElement, db=document.body;
20431             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20432                 t = dde.scrollTop;
20433                 l = dde.scrollLeft;
20434             } else if (db) {
20435                 t = db.scrollTop;
20436                 l = db.scrollLeft;
20437             } else {
20438
20439             }
20440             return { top: t, left: l };
20441         },
20442
20443         /**
20444          * Returns the specified element style property
20445          * @method getStyle
20446          * @param {HTMLElement} el          the element
20447          * @param {string}      styleProp   the style property
20448          * @return {string} The value of the style property
20449          * @deprecated use Roo.lib.Dom.getStyle
20450          * @static
20451          */
20452         getStyle: function(el, styleProp) {
20453             return Roo.fly(el).getStyle(styleProp);
20454         },
20455
20456         /**
20457          * Gets the scrollTop
20458          * @method getScrollTop
20459          * @return {int} the document's scrollTop
20460          * @static
20461          */
20462         getScrollTop: function () { return this.getScroll().top; },
20463
20464         /**
20465          * Gets the scrollLeft
20466          * @method getScrollLeft
20467          * @return {int} the document's scrollTop
20468          * @static
20469          */
20470         getScrollLeft: function () { return this.getScroll().left; },
20471
20472         /**
20473          * Sets the x/y position of an element to the location of the
20474          * target element.
20475          * @method moveToEl
20476          * @param {HTMLElement} moveEl      The element to move
20477          * @param {HTMLElement} targetEl    The position reference element
20478          * @static
20479          */
20480         moveToEl: function (moveEl, targetEl) {
20481             var aCoord = Roo.lib.Dom.getXY(targetEl);
20482             Roo.lib.Dom.setXY(moveEl, aCoord);
20483         },
20484
20485         /**
20486          * Numeric array sort function
20487          * @method numericSort
20488          * @static
20489          */
20490         numericSort: function(a, b) { return (a - b); },
20491
20492         /**
20493          * Internal counter
20494          * @property _timeoutCount
20495          * @private
20496          * @static
20497          */
20498         _timeoutCount: 0,
20499
20500         /**
20501          * Trying to make the load order less important.  Without this we get
20502          * an error if this file is loaded before the Event Utility.
20503          * @method _addListeners
20504          * @private
20505          * @static
20506          */
20507         _addListeners: function() {
20508             var DDM = Roo.dd.DDM;
20509             if ( Roo.lib.Event && document ) {
20510                 DDM._onLoad();
20511             } else {
20512                 if (DDM._timeoutCount > 2000) {
20513                 } else {
20514                     setTimeout(DDM._addListeners, 10);
20515                     if (document && document.body) {
20516                         DDM._timeoutCount += 1;
20517                     }
20518                 }
20519             }
20520         },
20521
20522         /**
20523          * Recursively searches the immediate parent and all child nodes for
20524          * the handle element in order to determine wheter or not it was
20525          * clicked.
20526          * @method handleWasClicked
20527          * @param node the html element to inspect
20528          * @static
20529          */
20530         handleWasClicked: function(node, id) {
20531             if (this.isHandle(id, node.id)) {
20532                 return true;
20533             } else {
20534                 // check to see if this is a text node child of the one we want
20535                 var p = node.parentNode;
20536
20537                 while (p) {
20538                     if (this.isHandle(id, p.id)) {
20539                         return true;
20540                     } else {
20541                         p = p.parentNode;
20542                     }
20543                 }
20544             }
20545
20546             return false;
20547         }
20548
20549     };
20550
20551 }();
20552
20553 // shorter alias, save a few bytes
20554 Roo.dd.DDM = Roo.dd.DragDropMgr;
20555 Roo.dd.DDM._addListeners();
20556
20557 }/*
20558  * Based on:
20559  * Ext JS Library 1.1.1
20560  * Copyright(c) 2006-2007, Ext JS, LLC.
20561  *
20562  * Originally Released Under LGPL - original licence link has changed is not relivant.
20563  *
20564  * Fork - LGPL
20565  * <script type="text/javascript">
20566  */
20567
20568 /**
20569  * @class Roo.dd.DD
20570  * A DragDrop implementation where the linked element follows the
20571  * mouse cursor during a drag.
20572  * @extends Roo.dd.DragDrop
20573  * @constructor
20574  * @param {String} id the id of the linked element
20575  * @param {String} sGroup the group of related DragDrop items
20576  * @param {object} config an object containing configurable attributes
20577  *                Valid properties for DD:
20578  *                    scroll
20579  */
20580 Roo.dd.DD = function(id, sGroup, config) {
20581     if (id) {
20582         this.init(id, sGroup, config);
20583     }
20584 };
20585
20586 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20587
20588     /**
20589      * When set to true, the utility automatically tries to scroll the browser
20590      * window wehn a drag and drop element is dragged near the viewport boundary.
20591      * Defaults to true.
20592      * @property scroll
20593      * @type boolean
20594      */
20595     scroll: true,
20596
20597     /**
20598      * Sets the pointer offset to the distance between the linked element's top
20599      * left corner and the location the element was clicked
20600      * @method autoOffset
20601      * @param {int} iPageX the X coordinate of the click
20602      * @param {int} iPageY the Y coordinate of the click
20603      */
20604     autoOffset: function(iPageX, iPageY) {
20605         var x = iPageX - this.startPageX;
20606         var y = iPageY - this.startPageY;
20607         this.setDelta(x, y);
20608     },
20609
20610     /**
20611      * Sets the pointer offset.  You can call this directly to force the
20612      * offset to be in a particular location (e.g., pass in 0,0 to set it
20613      * to the center of the object)
20614      * @method setDelta
20615      * @param {int} iDeltaX the distance from the left
20616      * @param {int} iDeltaY the distance from the top
20617      */
20618     setDelta: function(iDeltaX, iDeltaY) {
20619         this.deltaX = iDeltaX;
20620         this.deltaY = iDeltaY;
20621     },
20622
20623     /**
20624      * Sets the drag element to the location of the mousedown or click event,
20625      * maintaining the cursor location relative to the location on the element
20626      * that was clicked.  Override this if you want to place the element in a
20627      * location other than where the cursor is.
20628      * @method setDragElPos
20629      * @param {int} iPageX the X coordinate of the mousedown or drag event
20630      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20631      */
20632     setDragElPos: function(iPageX, iPageY) {
20633         // the first time we do this, we are going to check to make sure
20634         // the element has css positioning
20635
20636         var el = this.getDragEl();
20637         this.alignElWithMouse(el, iPageX, iPageY);
20638     },
20639
20640     /**
20641      * Sets the element to the location of the mousedown or click event,
20642      * maintaining the cursor location relative to the location on the element
20643      * that was clicked.  Override this if you want to place the element in a
20644      * location other than where the cursor is.
20645      * @method alignElWithMouse
20646      * @param {HTMLElement} el the element to move
20647      * @param {int} iPageX the X coordinate of the mousedown or drag event
20648      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20649      */
20650     alignElWithMouse: function(el, iPageX, iPageY) {
20651         var oCoord = this.getTargetCoord(iPageX, iPageY);
20652         var fly = el.dom ? el : Roo.fly(el);
20653         if (!this.deltaSetXY) {
20654             var aCoord = [oCoord.x, oCoord.y];
20655             fly.setXY(aCoord);
20656             var newLeft = fly.getLeft(true);
20657             var newTop  = fly.getTop(true);
20658             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20659         } else {
20660             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20661         }
20662
20663         this.cachePosition(oCoord.x, oCoord.y);
20664         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20665         return oCoord;
20666     },
20667
20668     /**
20669      * Saves the most recent position so that we can reset the constraints and
20670      * tick marks on-demand.  We need to know this so that we can calculate the
20671      * number of pixels the element is offset from its original position.
20672      * @method cachePosition
20673      * @param iPageX the current x position (optional, this just makes it so we
20674      * don't have to look it up again)
20675      * @param iPageY the current y position (optional, this just makes it so we
20676      * don't have to look it up again)
20677      */
20678     cachePosition: function(iPageX, iPageY) {
20679         if (iPageX) {
20680             this.lastPageX = iPageX;
20681             this.lastPageY = iPageY;
20682         } else {
20683             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20684             this.lastPageX = aCoord[0];
20685             this.lastPageY = aCoord[1];
20686         }
20687     },
20688
20689     /**
20690      * Auto-scroll the window if the dragged object has been moved beyond the
20691      * visible window boundary.
20692      * @method autoScroll
20693      * @param {int} x the drag element's x position
20694      * @param {int} y the drag element's y position
20695      * @param {int} h the height of the drag element
20696      * @param {int} w the width of the drag element
20697      * @private
20698      */
20699     autoScroll: function(x, y, h, w) {
20700
20701         if (this.scroll) {
20702             // The client height
20703             var clientH = Roo.lib.Dom.getViewWidth();
20704
20705             // The client width
20706             var clientW = Roo.lib.Dom.getViewHeight();
20707
20708             // The amt scrolled down
20709             var st = this.DDM.getScrollTop();
20710
20711             // The amt scrolled right
20712             var sl = this.DDM.getScrollLeft();
20713
20714             // Location of the bottom of the element
20715             var bot = h + y;
20716
20717             // Location of the right of the element
20718             var right = w + x;
20719
20720             // The distance from the cursor to the bottom of the visible area,
20721             // adjusted so that we don't scroll if the cursor is beyond the
20722             // element drag constraints
20723             var toBot = (clientH + st - y - this.deltaY);
20724
20725             // The distance from the cursor to the right of the visible area
20726             var toRight = (clientW + sl - x - this.deltaX);
20727
20728
20729             // How close to the edge the cursor must be before we scroll
20730             // var thresh = (document.all) ? 100 : 40;
20731             var thresh = 40;
20732
20733             // How many pixels to scroll per autoscroll op.  This helps to reduce
20734             // clunky scrolling. IE is more sensitive about this ... it needs this
20735             // value to be higher.
20736             var scrAmt = (document.all) ? 80 : 30;
20737
20738             // Scroll down if we are near the bottom of the visible page and the
20739             // obj extends below the crease
20740             if ( bot > clientH && toBot < thresh ) {
20741                 window.scrollTo(sl, st + scrAmt);
20742             }
20743
20744             // Scroll up if the window is scrolled down and the top of the object
20745             // goes above the top border
20746             if ( y < st && st > 0 && y - st < thresh ) {
20747                 window.scrollTo(sl, st - scrAmt);
20748             }
20749
20750             // Scroll right if the obj is beyond the right border and the cursor is
20751             // near the border.
20752             if ( right > clientW && toRight < thresh ) {
20753                 window.scrollTo(sl + scrAmt, st);
20754             }
20755
20756             // Scroll left if the window has been scrolled to the right and the obj
20757             // extends past the left border
20758             if ( x < sl && sl > 0 && x - sl < thresh ) {
20759                 window.scrollTo(sl - scrAmt, st);
20760             }
20761         }
20762     },
20763
20764     /**
20765      * Finds the location the element should be placed if we want to move
20766      * it to where the mouse location less the click offset would place us.
20767      * @method getTargetCoord
20768      * @param {int} iPageX the X coordinate of the click
20769      * @param {int} iPageY the Y coordinate of the click
20770      * @return an object that contains the coordinates (Object.x and Object.y)
20771      * @private
20772      */
20773     getTargetCoord: function(iPageX, iPageY) {
20774
20775
20776         var x = iPageX - this.deltaX;
20777         var y = iPageY - this.deltaY;
20778
20779         if (this.constrainX) {
20780             if (x < this.minX) { x = this.minX; }
20781             if (x > this.maxX) { x = this.maxX; }
20782         }
20783
20784         if (this.constrainY) {
20785             if (y < this.minY) { y = this.minY; }
20786             if (y > this.maxY) { y = this.maxY; }
20787         }
20788
20789         x = this.getTick(x, this.xTicks);
20790         y = this.getTick(y, this.yTicks);
20791
20792
20793         return {x:x, y:y};
20794     },
20795
20796     /*
20797      * Sets up config options specific to this class. Overrides
20798      * Roo.dd.DragDrop, but all versions of this method through the
20799      * inheritance chain are called
20800      */
20801     applyConfig: function() {
20802         Roo.dd.DD.superclass.applyConfig.call(this);
20803         this.scroll = (this.config.scroll !== false);
20804     },
20805
20806     /*
20807      * Event that fires prior to the onMouseDown event.  Overrides
20808      * Roo.dd.DragDrop.
20809      */
20810     b4MouseDown: function(e) {
20811         // this.resetConstraints();
20812         this.autoOffset(e.getPageX(),
20813                             e.getPageY());
20814     },
20815
20816     /*
20817      * Event that fires prior to the onDrag event.  Overrides
20818      * Roo.dd.DragDrop.
20819      */
20820     b4Drag: function(e) {
20821         this.setDragElPos(e.getPageX(),
20822                             e.getPageY());
20823     },
20824
20825     toString: function() {
20826         return ("DD " + this.id);
20827     }
20828
20829     //////////////////////////////////////////////////////////////////////////
20830     // Debugging ygDragDrop events that can be overridden
20831     //////////////////////////////////////////////////////////////////////////
20832     /*
20833     startDrag: function(x, y) {
20834     },
20835
20836     onDrag: function(e) {
20837     },
20838
20839     onDragEnter: function(e, id) {
20840     },
20841
20842     onDragOver: function(e, id) {
20843     },
20844
20845     onDragOut: function(e, id) {
20846     },
20847
20848     onDragDrop: function(e, id) {
20849     },
20850
20851     endDrag: function(e) {
20852     }
20853
20854     */
20855
20856 });/*
20857  * Based on:
20858  * Ext JS Library 1.1.1
20859  * Copyright(c) 2006-2007, Ext JS, LLC.
20860  *
20861  * Originally Released Under LGPL - original licence link has changed is not relivant.
20862  *
20863  * Fork - LGPL
20864  * <script type="text/javascript">
20865  */
20866
20867 /**
20868  * @class Roo.dd.DDProxy
20869  * A DragDrop implementation that inserts an empty, bordered div into
20870  * the document that follows the cursor during drag operations.  At the time of
20871  * the click, the frame div is resized to the dimensions of the linked html
20872  * element, and moved to the exact location of the linked element.
20873  *
20874  * References to the "frame" element refer to the single proxy element that
20875  * was created to be dragged in place of all DDProxy elements on the
20876  * page.
20877  *
20878  * @extends Roo.dd.DD
20879  * @constructor
20880  * @param {String} id the id of the linked html element
20881  * @param {String} sGroup the group of related DragDrop objects
20882  * @param {object} config an object containing configurable attributes
20883  *                Valid properties for DDProxy in addition to those in DragDrop:
20884  *                   resizeFrame, centerFrame, dragElId
20885  */
20886 Roo.dd.DDProxy = function(id, sGroup, config) {
20887     if (id) {
20888         this.init(id, sGroup, config);
20889         this.initFrame();
20890     }
20891 };
20892
20893 /**
20894  * The default drag frame div id
20895  * @property Roo.dd.DDProxy.dragElId
20896  * @type String
20897  * @static
20898  */
20899 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20900
20901 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20902
20903     /**
20904      * By default we resize the drag frame to be the same size as the element
20905      * we want to drag (this is to get the frame effect).  We can turn it off
20906      * if we want a different behavior.
20907      * @property resizeFrame
20908      * @type boolean
20909      */
20910     resizeFrame: true,
20911
20912     /**
20913      * By default the frame is positioned exactly where the drag element is, so
20914      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20915      * you do not have constraints on the obj is to have the drag frame centered
20916      * around the cursor.  Set centerFrame to true for this effect.
20917      * @property centerFrame
20918      * @type boolean
20919      */
20920     centerFrame: false,
20921
20922     /**
20923      * Creates the proxy element if it does not yet exist
20924      * @method createFrame
20925      */
20926     createFrame: function() {
20927         var self = this;
20928         var body = document.body;
20929
20930         if (!body || !body.firstChild) {
20931             setTimeout( function() { self.createFrame(); }, 50 );
20932             return;
20933         }
20934
20935         var div = this.getDragEl();
20936
20937         if (!div) {
20938             div    = document.createElement("div");
20939             div.id = this.dragElId;
20940             var s  = div.style;
20941
20942             s.position   = "absolute";
20943             s.visibility = "hidden";
20944             s.cursor     = "move";
20945             s.border     = "2px solid #aaa";
20946             s.zIndex     = 999;
20947
20948             // appendChild can blow up IE if invoked prior to the window load event
20949             // while rendering a table.  It is possible there are other scenarios
20950             // that would cause this to happen as well.
20951             body.insertBefore(div, body.firstChild);
20952         }
20953     },
20954
20955     /**
20956      * Initialization for the drag frame element.  Must be called in the
20957      * constructor of all subclasses
20958      * @method initFrame
20959      */
20960     initFrame: function() {
20961         this.createFrame();
20962     },
20963
20964     applyConfig: function() {
20965         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20966
20967         this.resizeFrame = (this.config.resizeFrame !== false);
20968         this.centerFrame = (this.config.centerFrame);
20969         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20970     },
20971
20972     /**
20973      * Resizes the drag frame to the dimensions of the clicked object, positions
20974      * it over the object, and finally displays it
20975      * @method showFrame
20976      * @param {int} iPageX X click position
20977      * @param {int} iPageY Y click position
20978      * @private
20979      */
20980     showFrame: function(iPageX, iPageY) {
20981         var el = this.getEl();
20982         var dragEl = this.getDragEl();
20983         var s = dragEl.style;
20984
20985         this._resizeProxy();
20986
20987         if (this.centerFrame) {
20988             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20989                            Math.round(parseInt(s.height, 10)/2) );
20990         }
20991
20992         this.setDragElPos(iPageX, iPageY);
20993
20994         Roo.fly(dragEl).show();
20995     },
20996
20997     /**
20998      * The proxy is automatically resized to the dimensions of the linked
20999      * element when a drag is initiated, unless resizeFrame is set to false
21000      * @method _resizeProxy
21001      * @private
21002      */
21003     _resizeProxy: function() {
21004         if (this.resizeFrame) {
21005             var el = this.getEl();
21006             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21007         }
21008     },
21009
21010     // overrides Roo.dd.DragDrop
21011     b4MouseDown: function(e) {
21012         var x = e.getPageX();
21013         var y = e.getPageY();
21014         this.autoOffset(x, y);
21015         this.setDragElPos(x, y);
21016     },
21017
21018     // overrides Roo.dd.DragDrop
21019     b4StartDrag: function(x, y) {
21020         // show the drag frame
21021         this.showFrame(x, y);
21022     },
21023
21024     // overrides Roo.dd.DragDrop
21025     b4EndDrag: function(e) {
21026         Roo.fly(this.getDragEl()).hide();
21027     },
21028
21029     // overrides Roo.dd.DragDrop
21030     // By default we try to move the element to the last location of the frame.
21031     // This is so that the default behavior mirrors that of Roo.dd.DD.
21032     endDrag: function(e) {
21033
21034         var lel = this.getEl();
21035         var del = this.getDragEl();
21036
21037         // Show the drag frame briefly so we can get its position
21038         del.style.visibility = "";
21039
21040         this.beforeMove();
21041         // Hide the linked element before the move to get around a Safari
21042         // rendering bug.
21043         lel.style.visibility = "hidden";
21044         Roo.dd.DDM.moveToEl(lel, del);
21045         del.style.visibility = "hidden";
21046         lel.style.visibility = "";
21047
21048         this.afterDrag();
21049     },
21050
21051     beforeMove : function(){
21052
21053     },
21054
21055     afterDrag : function(){
21056
21057     },
21058
21059     toString: function() {
21060         return ("DDProxy " + this.id);
21061     }
21062
21063 });
21064 /*
21065  * Based on:
21066  * Ext JS Library 1.1.1
21067  * Copyright(c) 2006-2007, Ext JS, LLC.
21068  *
21069  * Originally Released Under LGPL - original licence link has changed is not relivant.
21070  *
21071  * Fork - LGPL
21072  * <script type="text/javascript">
21073  */
21074
21075  /**
21076  * @class Roo.dd.DDTarget
21077  * A DragDrop implementation that does not move, but can be a drop
21078  * target.  You would get the same result by simply omitting implementation
21079  * for the event callbacks, but this way we reduce the processing cost of the
21080  * event listener and the callbacks.
21081  * @extends Roo.dd.DragDrop
21082  * @constructor
21083  * @param {String} id the id of the element that is a drop target
21084  * @param {String} sGroup the group of related DragDrop objects
21085  * @param {object} config an object containing configurable attributes
21086  *                 Valid properties for DDTarget in addition to those in
21087  *                 DragDrop:
21088  *                    none
21089  */
21090 Roo.dd.DDTarget = function(id, sGroup, config) {
21091     if (id) {
21092         this.initTarget(id, sGroup, config);
21093     }
21094     if (config.listeners || config.events) { 
21095        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21096             listeners : config.listeners || {}, 
21097             events : config.events || {} 
21098         });    
21099     }
21100 };
21101
21102 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21103 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21104     toString: function() {
21105         return ("DDTarget " + this.id);
21106     }
21107 });
21108 /*
21109  * Based on:
21110  * Ext JS Library 1.1.1
21111  * Copyright(c) 2006-2007, Ext JS, LLC.
21112  *
21113  * Originally Released Under LGPL - original licence link has changed is not relivant.
21114  *
21115  * Fork - LGPL
21116  * <script type="text/javascript">
21117  */
21118  
21119
21120 /**
21121  * @class Roo.dd.ScrollManager
21122  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21123  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21124  * @singleton
21125  */
21126 Roo.dd.ScrollManager = function(){
21127     var ddm = Roo.dd.DragDropMgr;
21128     var els = {};
21129     var dragEl = null;
21130     var proc = {};
21131     
21132     
21133     
21134     var onStop = function(e){
21135         dragEl = null;
21136         clearProc();
21137     };
21138     
21139     var triggerRefresh = function(){
21140         if(ddm.dragCurrent){
21141              ddm.refreshCache(ddm.dragCurrent.groups);
21142         }
21143     };
21144     
21145     var doScroll = function(){
21146         if(ddm.dragCurrent){
21147             var dds = Roo.dd.ScrollManager;
21148             if(!dds.animate){
21149                 if(proc.el.scroll(proc.dir, dds.increment)){
21150                     triggerRefresh();
21151                 }
21152             }else{
21153                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21154             }
21155         }
21156     };
21157     
21158     var clearProc = function(){
21159         if(proc.id){
21160             clearInterval(proc.id);
21161         }
21162         proc.id = 0;
21163         proc.el = null;
21164         proc.dir = "";
21165     };
21166     
21167     var startProc = function(el, dir){
21168          Roo.log('scroll startproc');
21169         clearProc();
21170         proc.el = el;
21171         proc.dir = dir;
21172         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21173     };
21174     
21175     var onFire = function(e, isDrop){
21176        
21177         if(isDrop || !ddm.dragCurrent){ return; }
21178         var dds = Roo.dd.ScrollManager;
21179         if(!dragEl || dragEl != ddm.dragCurrent){
21180             dragEl = ddm.dragCurrent;
21181             // refresh regions on drag start
21182             dds.refreshCache();
21183         }
21184         
21185         var xy = Roo.lib.Event.getXY(e);
21186         var pt = new Roo.lib.Point(xy[0], xy[1]);
21187         for(var id in els){
21188             var el = els[id], r = el._region;
21189             if(r && r.contains(pt) && el.isScrollable()){
21190                 if(r.bottom - pt.y <= dds.thresh){
21191                     if(proc.el != el){
21192                         startProc(el, "down");
21193                     }
21194                     return;
21195                 }else if(r.right - pt.x <= dds.thresh){
21196                     if(proc.el != el){
21197                         startProc(el, "left");
21198                     }
21199                     return;
21200                 }else if(pt.y - r.top <= dds.thresh){
21201                     if(proc.el != el){
21202                         startProc(el, "up");
21203                     }
21204                     return;
21205                 }else if(pt.x - r.left <= dds.thresh){
21206                     if(proc.el != el){
21207                         startProc(el, "right");
21208                     }
21209                     return;
21210                 }
21211             }
21212         }
21213         clearProc();
21214     };
21215     
21216     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21217     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21218     
21219     return {
21220         /**
21221          * Registers new overflow element(s) to auto scroll
21222          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21223          */
21224         register : function(el){
21225             if(el instanceof Array){
21226                 for(var i = 0, len = el.length; i < len; i++) {
21227                         this.register(el[i]);
21228                 }
21229             }else{
21230                 el = Roo.get(el);
21231                 els[el.id] = el;
21232             }
21233             Roo.dd.ScrollManager.els = els;
21234         },
21235         
21236         /**
21237          * Unregisters overflow element(s) so they are no longer scrolled
21238          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21239          */
21240         unregister : function(el){
21241             if(el instanceof Array){
21242                 for(var i = 0, len = el.length; i < len; i++) {
21243                         this.unregister(el[i]);
21244                 }
21245             }else{
21246                 el = Roo.get(el);
21247                 delete els[el.id];
21248             }
21249         },
21250         
21251         /**
21252          * The number of pixels from the edge of a container the pointer needs to be to 
21253          * trigger scrolling (defaults to 25)
21254          * @type Number
21255          */
21256         thresh : 25,
21257         
21258         /**
21259          * The number of pixels to scroll in each scroll increment (defaults to 50)
21260          * @type Number
21261          */
21262         increment : 100,
21263         
21264         /**
21265          * The frequency of scrolls in milliseconds (defaults to 500)
21266          * @type Number
21267          */
21268         frequency : 500,
21269         
21270         /**
21271          * True to animate the scroll (defaults to true)
21272          * @type Boolean
21273          */
21274         animate: true,
21275         
21276         /**
21277          * The animation duration in seconds - 
21278          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21279          * @type Number
21280          */
21281         animDuration: .4,
21282         
21283         /**
21284          * Manually trigger a cache refresh.
21285          */
21286         refreshCache : function(){
21287             for(var id in els){
21288                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21289                     els[id]._region = els[id].getRegion();
21290                 }
21291             }
21292         }
21293     };
21294 }();/*
21295  * Based on:
21296  * Ext JS Library 1.1.1
21297  * Copyright(c) 2006-2007, Ext JS, LLC.
21298  *
21299  * Originally Released Under LGPL - original licence link has changed is not relivant.
21300  *
21301  * Fork - LGPL
21302  * <script type="text/javascript">
21303  */
21304  
21305
21306 /**
21307  * @class Roo.dd.Registry
21308  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21309  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21310  * @singleton
21311  */
21312 Roo.dd.Registry = function(){
21313     var elements = {}; 
21314     var handles = {}; 
21315     var autoIdSeed = 0;
21316
21317     var getId = function(el, autogen){
21318         if(typeof el == "string"){
21319             return el;
21320         }
21321         var id = el.id;
21322         if(!id && autogen !== false){
21323             id = "roodd-" + (++autoIdSeed);
21324             el.id = id;
21325         }
21326         return id;
21327     };
21328     
21329     return {
21330     /**
21331      * Register a drag drop element
21332      * @param {String|HTMLElement} element The id or DOM node to register
21333      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21334      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21335      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21336      * populated in the data object (if applicable):
21337      * <pre>
21338 Value      Description<br />
21339 ---------  ------------------------------------------<br />
21340 handles    Array of DOM nodes that trigger dragging<br />
21341            for the element being registered<br />
21342 isHandle   True if the element passed in triggers<br />
21343            dragging itself, else false
21344 </pre>
21345      */
21346         register : function(el, data){
21347             data = data || {};
21348             if(typeof el == "string"){
21349                 el = document.getElementById(el);
21350             }
21351             data.ddel = el;
21352             elements[getId(el)] = data;
21353             if(data.isHandle !== false){
21354                 handles[data.ddel.id] = data;
21355             }
21356             if(data.handles){
21357                 var hs = data.handles;
21358                 for(var i = 0, len = hs.length; i < len; i++){
21359                         handles[getId(hs[i])] = data;
21360                 }
21361             }
21362         },
21363
21364     /**
21365      * Unregister a drag drop element
21366      * @param {String|HTMLElement}  element The id or DOM node to unregister
21367      */
21368         unregister : function(el){
21369             var id = getId(el, false);
21370             var data = elements[id];
21371             if(data){
21372                 delete elements[id];
21373                 if(data.handles){
21374                     var hs = data.handles;
21375                     for(var i = 0, len = hs.length; i < len; i++){
21376                         delete handles[getId(hs[i], false)];
21377                     }
21378                 }
21379             }
21380         },
21381
21382     /**
21383      * Returns the handle registered for a DOM Node by id
21384      * @param {String|HTMLElement} id The DOM node or id to look up
21385      * @return {Object} handle The custom handle data
21386      */
21387         getHandle : function(id){
21388             if(typeof id != "string"){ // must be element?
21389                 id = id.id;
21390             }
21391             return handles[id];
21392         },
21393
21394     /**
21395      * Returns the handle that is registered for the DOM node that is the target of the event
21396      * @param {Event} e The event
21397      * @return {Object} handle The custom handle data
21398      */
21399         getHandleFromEvent : function(e){
21400             var t = Roo.lib.Event.getTarget(e);
21401             return t ? handles[t.id] : null;
21402         },
21403
21404     /**
21405      * Returns a custom data object that is registered for a DOM node by id
21406      * @param {String|HTMLElement} id The DOM node or id to look up
21407      * @return {Object} data The custom data
21408      */
21409         getTarget : function(id){
21410             if(typeof id != "string"){ // must be element?
21411                 id = id.id;
21412             }
21413             return elements[id];
21414         },
21415
21416     /**
21417      * Returns a custom data object that is registered for the DOM node that is the target of the event
21418      * @param {Event} e The event
21419      * @return {Object} data The custom data
21420      */
21421         getTargetFromEvent : function(e){
21422             var t = Roo.lib.Event.getTarget(e);
21423             return t ? elements[t.id] || handles[t.id] : null;
21424         }
21425     };
21426 }();/*
21427  * Based on:
21428  * Ext JS Library 1.1.1
21429  * Copyright(c) 2006-2007, Ext JS, LLC.
21430  *
21431  * Originally Released Under LGPL - original licence link has changed is not relivant.
21432  *
21433  * Fork - LGPL
21434  * <script type="text/javascript">
21435  */
21436  
21437
21438 /**
21439  * @class Roo.dd.StatusProxy
21440  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21441  * default drag proxy used by all Roo.dd components.
21442  * @constructor
21443  * @param {Object} config
21444  */
21445 Roo.dd.StatusProxy = function(config){
21446     Roo.apply(this, config);
21447     this.id = this.id || Roo.id();
21448     this.el = new Roo.Layer({
21449         dh: {
21450             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21451                 {tag: "div", cls: "x-dd-drop-icon"},
21452                 {tag: "div", cls: "x-dd-drag-ghost"}
21453             ]
21454         }, 
21455         shadow: !config || config.shadow !== false
21456     });
21457     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21458     this.dropStatus = this.dropNotAllowed;
21459 };
21460
21461 Roo.dd.StatusProxy.prototype = {
21462     /**
21463      * @cfg {String} dropAllowed
21464      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21465      */
21466     dropAllowed : "x-dd-drop-ok",
21467     /**
21468      * @cfg {String} dropNotAllowed
21469      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21470      */
21471     dropNotAllowed : "x-dd-drop-nodrop",
21472
21473     /**
21474      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21475      * over the current target element.
21476      * @param {String} cssClass The css class for the new drop status indicator image
21477      */
21478     setStatus : function(cssClass){
21479         cssClass = cssClass || this.dropNotAllowed;
21480         if(this.dropStatus != cssClass){
21481             this.el.replaceClass(this.dropStatus, cssClass);
21482             this.dropStatus = cssClass;
21483         }
21484     },
21485
21486     /**
21487      * Resets the status indicator to the default dropNotAllowed value
21488      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21489      */
21490     reset : function(clearGhost){
21491         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21492         this.dropStatus = this.dropNotAllowed;
21493         if(clearGhost){
21494             this.ghost.update("");
21495         }
21496     },
21497
21498     /**
21499      * Updates the contents of the ghost element
21500      * @param {String} html The html that will replace the current innerHTML of the ghost element
21501      */
21502     update : function(html){
21503         if(typeof html == "string"){
21504             this.ghost.update(html);
21505         }else{
21506             this.ghost.update("");
21507             html.style.margin = "0";
21508             this.ghost.dom.appendChild(html);
21509         }
21510         // ensure float = none set?? cant remember why though.
21511         var el = this.ghost.dom.firstChild;
21512                 if(el){
21513                         Roo.fly(el).setStyle('float', 'none');
21514                 }
21515     },
21516     
21517     /**
21518      * Returns the underlying proxy {@link Roo.Layer}
21519      * @return {Roo.Layer} el
21520     */
21521     getEl : function(){
21522         return this.el;
21523     },
21524
21525     /**
21526      * Returns the ghost element
21527      * @return {Roo.Element} el
21528      */
21529     getGhost : function(){
21530         return this.ghost;
21531     },
21532
21533     /**
21534      * Hides the proxy
21535      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21536      */
21537     hide : function(clear){
21538         this.el.hide();
21539         if(clear){
21540             this.reset(true);
21541         }
21542     },
21543
21544     /**
21545      * Stops the repair animation if it's currently running
21546      */
21547     stop : function(){
21548         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21549             this.anim.stop();
21550         }
21551     },
21552
21553     /**
21554      * Displays this proxy
21555      */
21556     show : function(){
21557         this.el.show();
21558     },
21559
21560     /**
21561      * Force the Layer to sync its shadow and shim positions to the element
21562      */
21563     sync : function(){
21564         this.el.sync();
21565     },
21566
21567     /**
21568      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21569      * invalid drop operation by the item being dragged.
21570      * @param {Array} xy The XY position of the element ([x, y])
21571      * @param {Function} callback The function to call after the repair is complete
21572      * @param {Object} scope The scope in which to execute the callback
21573      */
21574     repair : function(xy, callback, scope){
21575         this.callback = callback;
21576         this.scope = scope;
21577         if(xy && this.animRepair !== false){
21578             this.el.addClass("x-dd-drag-repair");
21579             this.el.hideUnders(true);
21580             this.anim = this.el.shift({
21581                 duration: this.repairDuration || .5,
21582                 easing: 'easeOut',
21583                 xy: xy,
21584                 stopFx: true,
21585                 callback: this.afterRepair,
21586                 scope: this
21587             });
21588         }else{
21589             this.afterRepair();
21590         }
21591     },
21592
21593     // private
21594     afterRepair : function(){
21595         this.hide(true);
21596         if(typeof this.callback == "function"){
21597             this.callback.call(this.scope || this);
21598         }
21599         this.callback = null;
21600         this.scope = null;
21601     }
21602 };/*
21603  * Based on:
21604  * Ext JS Library 1.1.1
21605  * Copyright(c) 2006-2007, Ext JS, LLC.
21606  *
21607  * Originally Released Under LGPL - original licence link has changed is not relivant.
21608  *
21609  * Fork - LGPL
21610  * <script type="text/javascript">
21611  */
21612
21613 /**
21614  * @class Roo.dd.DragSource
21615  * @extends Roo.dd.DDProxy
21616  * A simple class that provides the basic implementation needed to make any element draggable.
21617  * @constructor
21618  * @param {String/HTMLElement/Element} el The container element
21619  * @param {Object} config
21620  */
21621 Roo.dd.DragSource = function(el, config){
21622     this.el = Roo.get(el);
21623     this.dragData = {};
21624     
21625     Roo.apply(this, config);
21626     
21627     if(!this.proxy){
21628         this.proxy = new Roo.dd.StatusProxy();
21629     }
21630
21631     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21632           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21633     
21634     this.dragging = false;
21635 };
21636
21637 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21638     /**
21639      * @cfg {String} dropAllowed
21640      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21641      */
21642     dropAllowed : "x-dd-drop-ok",
21643     /**
21644      * @cfg {String} dropNotAllowed
21645      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21646      */
21647     dropNotAllowed : "x-dd-drop-nodrop",
21648
21649     /**
21650      * Returns the data object associated with this drag source
21651      * @return {Object} data An object containing arbitrary data
21652      */
21653     getDragData : function(e){
21654         return this.dragData;
21655     },
21656
21657     // private
21658     onDragEnter : function(e, id){
21659         var target = Roo.dd.DragDropMgr.getDDById(id);
21660         this.cachedTarget = target;
21661         if(this.beforeDragEnter(target, e, id) !== false){
21662             if(target.isNotifyTarget){
21663                 var status = target.notifyEnter(this, e, this.dragData);
21664                 this.proxy.setStatus(status);
21665             }else{
21666                 this.proxy.setStatus(this.dropAllowed);
21667             }
21668             
21669             if(this.afterDragEnter){
21670                 /**
21671                  * An empty function by default, but provided so that you can perform a custom action
21672                  * when the dragged item enters the drop target by providing an implementation.
21673                  * @param {Roo.dd.DragDrop} target The drop target
21674                  * @param {Event} e The event object
21675                  * @param {String} id The id of the dragged element
21676                  * @method afterDragEnter
21677                  */
21678                 this.afterDragEnter(target, e, id);
21679             }
21680         }
21681     },
21682
21683     /**
21684      * An empty function by default, but provided so that you can perform a custom action
21685      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21686      * @param {Roo.dd.DragDrop} target The drop target
21687      * @param {Event} e The event object
21688      * @param {String} id The id of the dragged element
21689      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21690      */
21691     beforeDragEnter : function(target, e, id){
21692         return true;
21693     },
21694
21695     // private
21696     alignElWithMouse: function() {
21697         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21698         this.proxy.sync();
21699     },
21700
21701     // private
21702     onDragOver : function(e, id){
21703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21704         if(this.beforeDragOver(target, e, id) !== false){
21705             if(target.isNotifyTarget){
21706                 var status = target.notifyOver(this, e, this.dragData);
21707                 this.proxy.setStatus(status);
21708             }
21709
21710             if(this.afterDragOver){
21711                 /**
21712                  * An empty function by default, but provided so that you can perform a custom action
21713                  * while the dragged item is over the drop target by providing an implementation.
21714                  * @param {Roo.dd.DragDrop} target The drop target
21715                  * @param {Event} e The event object
21716                  * @param {String} id The id of the dragged element
21717                  * @method afterDragOver
21718                  */
21719                 this.afterDragOver(target, e, id);
21720             }
21721         }
21722     },
21723
21724     /**
21725      * An empty function by default, but provided so that you can perform a custom action
21726      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21727      * @param {Roo.dd.DragDrop} target The drop target
21728      * @param {Event} e The event object
21729      * @param {String} id The id of the dragged element
21730      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21731      */
21732     beforeDragOver : function(target, e, id){
21733         return true;
21734     },
21735
21736     // private
21737     onDragOut : function(e, id){
21738         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21739         if(this.beforeDragOut(target, e, id) !== false){
21740             if(target.isNotifyTarget){
21741                 target.notifyOut(this, e, this.dragData);
21742             }
21743             this.proxy.reset();
21744             if(this.afterDragOut){
21745                 /**
21746                  * An empty function by default, but provided so that you can perform a custom action
21747                  * after the dragged item is dragged out of the target without dropping.
21748                  * @param {Roo.dd.DragDrop} target The drop target
21749                  * @param {Event} e The event object
21750                  * @param {String} id The id of the dragged element
21751                  * @method afterDragOut
21752                  */
21753                 this.afterDragOut(target, e, id);
21754             }
21755         }
21756         this.cachedTarget = null;
21757     },
21758
21759     /**
21760      * An empty function by default, but provided so that you can perform a custom action before the dragged
21761      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21762      * @param {Roo.dd.DragDrop} target The drop target
21763      * @param {Event} e The event object
21764      * @param {String} id The id of the dragged element
21765      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21766      */
21767     beforeDragOut : function(target, e, id){
21768         return true;
21769     },
21770     
21771     // private
21772     onDragDrop : function(e, id){
21773         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21774         if(this.beforeDragDrop(target, e, id) !== false){
21775             if(target.isNotifyTarget){
21776                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21777                     this.onValidDrop(target, e, id);
21778                 }else{
21779                     this.onInvalidDrop(target, e, id);
21780                 }
21781             }else{
21782                 this.onValidDrop(target, e, id);
21783             }
21784             
21785             if(this.afterDragDrop){
21786                 /**
21787                  * An empty function by default, but provided so that you can perform a custom action
21788                  * after a valid drag drop has occurred by providing an implementation.
21789                  * @param {Roo.dd.DragDrop} target The drop target
21790                  * @param {Event} e The event object
21791                  * @param {String} id The id of the dropped element
21792                  * @method afterDragDrop
21793                  */
21794                 this.afterDragDrop(target, e, id);
21795             }
21796         }
21797         delete this.cachedTarget;
21798     },
21799
21800     /**
21801      * An empty function by default, but provided so that you can perform a custom action before the dragged
21802      * item is dropped onto the target and optionally cancel the onDragDrop.
21803      * @param {Roo.dd.DragDrop} target The drop target
21804      * @param {Event} e The event object
21805      * @param {String} id The id of the dragged element
21806      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21807      */
21808     beforeDragDrop : function(target, e, id){
21809         return true;
21810     },
21811
21812     // private
21813     onValidDrop : function(target, e, id){
21814         this.hideProxy();
21815         if(this.afterValidDrop){
21816             /**
21817              * An empty function by default, but provided so that you can perform a custom action
21818              * after a valid drop has occurred by providing an implementation.
21819              * @param {Object} target The target DD 
21820              * @param {Event} e The event object
21821              * @param {String} id The id of the dropped element
21822              * @method afterInvalidDrop
21823              */
21824             this.afterValidDrop(target, e, id);
21825         }
21826     },
21827
21828     // private
21829     getRepairXY : function(e, data){
21830         return this.el.getXY();  
21831     },
21832
21833     // private
21834     onInvalidDrop : function(target, e, id){
21835         this.beforeInvalidDrop(target, e, id);
21836         if(this.cachedTarget){
21837             if(this.cachedTarget.isNotifyTarget){
21838                 this.cachedTarget.notifyOut(this, e, this.dragData);
21839             }
21840             this.cacheTarget = null;
21841         }
21842         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21843
21844         if(this.afterInvalidDrop){
21845             /**
21846              * An empty function by default, but provided so that you can perform a custom action
21847              * after an invalid drop has occurred by providing an implementation.
21848              * @param {Event} e The event object
21849              * @param {String} id The id of the dropped element
21850              * @method afterInvalidDrop
21851              */
21852             this.afterInvalidDrop(e, id);
21853         }
21854     },
21855
21856     // private
21857     afterRepair : function(){
21858         if(Roo.enableFx){
21859             this.el.highlight(this.hlColor || "c3daf9");
21860         }
21861         this.dragging = false;
21862     },
21863
21864     /**
21865      * An empty function by default, but provided so that you can perform a custom action after an invalid
21866      * drop has occurred.
21867      * @param {Roo.dd.DragDrop} target The drop target
21868      * @param {Event} e The event object
21869      * @param {String} id The id of the dragged element
21870      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21871      */
21872     beforeInvalidDrop : function(target, e, id){
21873         return true;
21874     },
21875
21876     // private
21877     handleMouseDown : function(e){
21878         if(this.dragging) {
21879             return;
21880         }
21881         var data = this.getDragData(e);
21882         if(data && this.onBeforeDrag(data, e) !== false){
21883             this.dragData = data;
21884             this.proxy.stop();
21885             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21886         } 
21887     },
21888
21889     /**
21890      * An empty function by default, but provided so that you can perform a custom action before the initial
21891      * drag event begins and optionally cancel it.
21892      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21893      * @param {Event} e The event object
21894      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21895      */
21896     onBeforeDrag : function(data, e){
21897         return true;
21898     },
21899
21900     /**
21901      * An empty function by default, but provided so that you can perform a custom action once the initial
21902      * drag event has begun.  The drag cannot be canceled from this function.
21903      * @param {Number} x The x position of the click on the dragged object
21904      * @param {Number} y The y position of the click on the dragged object
21905      */
21906     onStartDrag : Roo.emptyFn,
21907
21908     // private - YUI override
21909     startDrag : function(x, y){
21910         this.proxy.reset();
21911         this.dragging = true;
21912         this.proxy.update("");
21913         this.onInitDrag(x, y);
21914         this.proxy.show();
21915     },
21916
21917     // private
21918     onInitDrag : function(x, y){
21919         var clone = this.el.dom.cloneNode(true);
21920         clone.id = Roo.id(); // prevent duplicate ids
21921         this.proxy.update(clone);
21922         this.onStartDrag(x, y);
21923         return true;
21924     },
21925
21926     /**
21927      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21928      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21929      */
21930     getProxy : function(){
21931         return this.proxy;  
21932     },
21933
21934     /**
21935      * Hides the drag source's {@link Roo.dd.StatusProxy}
21936      */
21937     hideProxy : function(){
21938         this.proxy.hide();  
21939         this.proxy.reset(true);
21940         this.dragging = false;
21941     },
21942
21943     // private
21944     triggerCacheRefresh : function(){
21945         Roo.dd.DDM.refreshCache(this.groups);
21946     },
21947
21948     // private - override to prevent hiding
21949     b4EndDrag: function(e) {
21950     },
21951
21952     // private - override to prevent moving
21953     endDrag : function(e){
21954         this.onEndDrag(this.dragData, e);
21955     },
21956
21957     // private
21958     onEndDrag : function(data, e){
21959     },
21960     
21961     // private - pin to cursor
21962     autoOffset : function(x, y) {
21963         this.setDelta(-12, -20);
21964     }    
21965 });/*
21966  * Based on:
21967  * Ext JS Library 1.1.1
21968  * Copyright(c) 2006-2007, Ext JS, LLC.
21969  *
21970  * Originally Released Under LGPL - original licence link has changed is not relivant.
21971  *
21972  * Fork - LGPL
21973  * <script type="text/javascript">
21974  */
21975
21976
21977 /**
21978  * @class Roo.dd.DropTarget
21979  * @extends Roo.dd.DDTarget
21980  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21981  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21982  * @constructor
21983  * @param {String/HTMLElement/Element} el The container element
21984  * @param {Object} config
21985  */
21986 Roo.dd.DropTarget = function(el, config){
21987     this.el = Roo.get(el);
21988     
21989     var listeners = false; ;
21990     if (config && config.listeners) {
21991         listeners= config.listeners;
21992         delete config.listeners;
21993     }
21994     Roo.apply(this, config);
21995     
21996     if(this.containerScroll){
21997         Roo.dd.ScrollManager.register(this.el);
21998     }
21999     this.addEvents( {
22000          /**
22001          * @scope Roo.dd.DropTarget
22002          */
22003          
22004          /**
22005          * @event enter
22006          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22007          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22008          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22009          * 
22010          * IMPORTANT : it should set this.overClass and this.dropAllowed
22011          * 
22012          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22013          * @param {Event} e The event
22014          * @param {Object} data An object containing arbitrary data supplied by the drag source
22015          */
22016         "enter" : true,
22017         
22018          /**
22019          * @event over
22020          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22021          * This method will be called on every mouse movement while the drag source is over the drop target.
22022          * This default implementation simply returns the dropAllowed config value.
22023          * 
22024          * IMPORTANT : it should set this.dropAllowed
22025          * 
22026          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22027          * @param {Event} e The event
22028          * @param {Object} data An object containing arbitrary data supplied by the drag source
22029          
22030          */
22031         "over" : true,
22032         /**
22033          * @event out
22034          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22035          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22036          * overClass (if any) from the drop element.
22037          * 
22038          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22039          * @param {Event} e The event
22040          * @param {Object} data An object containing arbitrary data supplied by the drag source
22041          */
22042          "out" : true,
22043          
22044         /**
22045          * @event drop
22046          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22047          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22048          * implementation that does something to process the drop event and returns true so that the drag source's
22049          * repair action does not run.
22050          * 
22051          * IMPORTANT : it should set this.success
22052          * 
22053          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22054          * @param {Event} e The event
22055          * @param {Object} data An object containing arbitrary data supplied by the drag source
22056         */
22057          "drop" : true
22058     });
22059             
22060      
22061     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22062         this.el.dom, 
22063         this.ddGroup || this.group,
22064         {
22065             isTarget: true,
22066             listeners : listeners || {} 
22067            
22068         
22069         }
22070     );
22071
22072 };
22073
22074 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22075     /**
22076      * @cfg {String} overClass
22077      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22078      */
22079      /**
22080      * @cfg {String} ddGroup
22081      * The drag drop group to handle drop events for
22082      */
22083      
22084     /**
22085      * @cfg {String} dropAllowed
22086      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22087      */
22088     dropAllowed : "x-dd-drop-ok",
22089     /**
22090      * @cfg {String} dropNotAllowed
22091      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22092      */
22093     dropNotAllowed : "x-dd-drop-nodrop",
22094     /**
22095      * @cfg {boolean} success
22096      * set this after drop listener.. 
22097      */
22098     success : false,
22099     /**
22100      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22101      * if the drop point is valid for over/enter..
22102      */
22103     valid : false,
22104     // private
22105     isTarget : true,
22106
22107     // private
22108     isNotifyTarget : true,
22109     
22110     /**
22111      * @hide
22112      */
22113     notifyEnter : function(dd, e, data)
22114     {
22115         this.valid = true;
22116         this.fireEvent('enter', dd, e, data);
22117         if(this.overClass){
22118             this.el.addClass(this.overClass);
22119         }
22120         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22121             this.valid ? this.dropAllowed : this.dropNotAllowed
22122         );
22123     },
22124
22125     /**
22126      * @hide
22127      */
22128     notifyOver : function(dd, e, data)
22129     {
22130         this.valid = true;
22131         this.fireEvent('over', dd, e, data);
22132         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22133             this.valid ? this.dropAllowed : this.dropNotAllowed
22134         );
22135     },
22136
22137     /**
22138      * @hide
22139      */
22140     notifyOut : function(dd, e, data)
22141     {
22142         this.fireEvent('out', dd, e, data);
22143         if(this.overClass){
22144             this.el.removeClass(this.overClass);
22145         }
22146     },
22147
22148     /**
22149      * @hide
22150      */
22151     notifyDrop : function(dd, e, data)
22152     {
22153         this.success = false;
22154         this.fireEvent('drop', dd, e, data);
22155         return this.success;
22156     }
22157 });/*
22158  * Based on:
22159  * Ext JS Library 1.1.1
22160  * Copyright(c) 2006-2007, Ext JS, LLC.
22161  *
22162  * Originally Released Under LGPL - original licence link has changed is not relivant.
22163  *
22164  * Fork - LGPL
22165  * <script type="text/javascript">
22166  */
22167
22168
22169 /**
22170  * @class Roo.dd.DragZone
22171  * @extends Roo.dd.DragSource
22172  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22173  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22174  * @constructor
22175  * @param {String/HTMLElement/Element} el The container element
22176  * @param {Object} config
22177  */
22178 Roo.dd.DragZone = function(el, config){
22179     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22180     if(this.containerScroll){
22181         Roo.dd.ScrollManager.register(this.el);
22182     }
22183 };
22184
22185 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22186     /**
22187      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22188      * for auto scrolling during drag operations.
22189      */
22190     /**
22191      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22192      * method after a failed drop (defaults to "c3daf9" - light blue)
22193      */
22194
22195     /**
22196      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22197      * for a valid target to drag based on the mouse down. Override this method
22198      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22199      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22200      * @param {EventObject} e The mouse down event
22201      * @return {Object} The dragData
22202      */
22203     getDragData : function(e){
22204         return Roo.dd.Registry.getHandleFromEvent(e);
22205     },
22206     
22207     /**
22208      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22209      * this.dragData.ddel
22210      * @param {Number} x The x position of the click on the dragged object
22211      * @param {Number} y The y position of the click on the dragged object
22212      * @return {Boolean} true to continue the drag, false to cancel
22213      */
22214     onInitDrag : function(x, y){
22215         this.proxy.update(this.dragData.ddel.cloneNode(true));
22216         this.onStartDrag(x, y);
22217         return true;
22218     },
22219     
22220     /**
22221      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22222      */
22223     afterRepair : function(){
22224         if(Roo.enableFx){
22225             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22226         }
22227         this.dragging = false;
22228     },
22229
22230     /**
22231      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22232      * the XY of this.dragData.ddel
22233      * @param {EventObject} e The mouse up event
22234      * @return {Array} The xy location (e.g. [100, 200])
22235      */
22236     getRepairXY : function(e){
22237         return Roo.Element.fly(this.dragData.ddel).getXY();  
22238     }
22239 });/*
22240  * Based on:
22241  * Ext JS Library 1.1.1
22242  * Copyright(c) 2006-2007, Ext JS, LLC.
22243  *
22244  * Originally Released Under LGPL - original licence link has changed is not relivant.
22245  *
22246  * Fork - LGPL
22247  * <script type="text/javascript">
22248  */
22249 /**
22250  * @class Roo.dd.DropZone
22251  * @extends Roo.dd.DropTarget
22252  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22253  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22254  * @constructor
22255  * @param {String/HTMLElement/Element} el The container element
22256  * @param {Object} config
22257  */
22258 Roo.dd.DropZone = function(el, config){
22259     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22260 };
22261
22262 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22263     /**
22264      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22265      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22266      * provide your own custom lookup.
22267      * @param {Event} e The event
22268      * @return {Object} data The custom data
22269      */
22270     getTargetFromEvent : function(e){
22271         return Roo.dd.Registry.getTargetFromEvent(e);
22272     },
22273
22274     /**
22275      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22276      * that it has registered.  This method has no default implementation and should be overridden to provide
22277      * node-specific processing if necessary.
22278      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22279      * {@link #getTargetFromEvent} for this node)
22280      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22281      * @param {Event} e The event
22282      * @param {Object} data An object containing arbitrary data supplied by the drag source
22283      */
22284     onNodeEnter : function(n, dd, e, data){
22285         
22286     },
22287
22288     /**
22289      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22290      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22291      * overridden to provide the proper feedback.
22292      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22293      * {@link #getTargetFromEvent} for this node)
22294      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22295      * @param {Event} e The event
22296      * @param {Object} data An object containing arbitrary data supplied by the drag source
22297      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22298      * underlying {@link Roo.dd.StatusProxy} can be updated
22299      */
22300     onNodeOver : function(n, dd, e, data){
22301         return this.dropAllowed;
22302     },
22303
22304     /**
22305      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22306      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22307      * node-specific processing if necessary.
22308      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22309      * {@link #getTargetFromEvent} for this node)
22310      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22311      * @param {Event} e The event
22312      * @param {Object} data An object containing arbitrary data supplied by the drag source
22313      */
22314     onNodeOut : function(n, dd, e, data){
22315         
22316     },
22317
22318     /**
22319      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22320      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22321      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22322      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22323      * {@link #getTargetFromEvent} for this node)
22324      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22325      * @param {Event} e The event
22326      * @param {Object} data An object containing arbitrary data supplied by the drag source
22327      * @return {Boolean} True if the drop was valid, else false
22328      */
22329     onNodeDrop : function(n, dd, e, data){
22330         return false;
22331     },
22332
22333     /**
22334      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22335      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22336      * it should be overridden to provide the proper feedback if necessary.
22337      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22338      * @param {Event} e The event
22339      * @param {Object} data An object containing arbitrary data supplied by the drag source
22340      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22341      * underlying {@link Roo.dd.StatusProxy} can be updated
22342      */
22343     onContainerOver : function(dd, e, data){
22344         return this.dropNotAllowed;
22345     },
22346
22347     /**
22348      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22349      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22350      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22351      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22352      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22353      * @param {Event} e The event
22354      * @param {Object} data An object containing arbitrary data supplied by the drag source
22355      * @return {Boolean} True if the drop was valid, else false
22356      */
22357     onContainerDrop : function(dd, e, data){
22358         return false;
22359     },
22360
22361     /**
22362      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22363      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22364      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22365      * you should override this method and provide a custom implementation.
22366      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22367      * @param {Event} e The event
22368      * @param {Object} data An object containing arbitrary data supplied by the drag source
22369      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22370      * underlying {@link Roo.dd.StatusProxy} can be updated
22371      */
22372     notifyEnter : function(dd, e, data){
22373         return this.dropNotAllowed;
22374     },
22375
22376     /**
22377      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22378      * This method will be called on every mouse movement while the drag source is over the drop zone.
22379      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22380      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22381      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22382      * registered node, it will call {@link #onContainerOver}.
22383      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22384      * @param {Event} e The event
22385      * @param {Object} data An object containing arbitrary data supplied by the drag source
22386      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22387      * underlying {@link Roo.dd.StatusProxy} can be updated
22388      */
22389     notifyOver : function(dd, e, data){
22390         var n = this.getTargetFromEvent(e);
22391         if(!n){ // not over valid drop target
22392             if(this.lastOverNode){
22393                 this.onNodeOut(this.lastOverNode, dd, e, data);
22394                 this.lastOverNode = null;
22395             }
22396             return this.onContainerOver(dd, e, data);
22397         }
22398         if(this.lastOverNode != n){
22399             if(this.lastOverNode){
22400                 this.onNodeOut(this.lastOverNode, dd, e, data);
22401             }
22402             this.onNodeEnter(n, dd, e, data);
22403             this.lastOverNode = n;
22404         }
22405         return this.onNodeOver(n, dd, e, data);
22406     },
22407
22408     /**
22409      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22410      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22411      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22412      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22413      * @param {Event} e The event
22414      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22415      */
22416     notifyOut : function(dd, e, data){
22417         if(this.lastOverNode){
22418             this.onNodeOut(this.lastOverNode, dd, e, data);
22419             this.lastOverNode = null;
22420         }
22421     },
22422
22423     /**
22424      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22425      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22426      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22427      * otherwise it will call {@link #onContainerDrop}.
22428      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22429      * @param {Event} e The event
22430      * @param {Object} data An object containing arbitrary data supplied by the drag source
22431      * @return {Boolean} True if the drop was valid, else false
22432      */
22433     notifyDrop : function(dd, e, data){
22434         if(this.lastOverNode){
22435             this.onNodeOut(this.lastOverNode, dd, e, data);
22436             this.lastOverNode = null;
22437         }
22438         var n = this.getTargetFromEvent(e);
22439         return n ?
22440             this.onNodeDrop(n, dd, e, data) :
22441             this.onContainerDrop(dd, e, data);
22442     },
22443
22444     // private
22445     triggerCacheRefresh : function(){
22446         Roo.dd.DDM.refreshCache(this.groups);
22447     }  
22448 });/*
22449  * Based on:
22450  * Ext JS Library 1.1.1
22451  * Copyright(c) 2006-2007, Ext JS, LLC.
22452  *
22453  * Originally Released Under LGPL - original licence link has changed is not relivant.
22454  *
22455  * Fork - LGPL
22456  * <script type="text/javascript">
22457  */
22458
22459
22460 /**
22461  * @class Roo.data.SortTypes
22462  * @singleton
22463  * Defines the default sorting (casting?) comparison functions used when sorting data.
22464  */
22465 Roo.data.SortTypes = {
22466     /**
22467      * Default sort that does nothing
22468      * @param {Mixed} s The value being converted
22469      * @return {Mixed} The comparison value
22470      */
22471     none : function(s){
22472         return s;
22473     },
22474     
22475     /**
22476      * The regular expression used to strip tags
22477      * @type {RegExp}
22478      * @property
22479      */
22480     stripTagsRE : /<\/?[^>]+>/gi,
22481     
22482     /**
22483      * Strips all HTML tags to sort on text only
22484      * @param {Mixed} s The value being converted
22485      * @return {String} The comparison value
22486      */
22487     asText : function(s){
22488         return String(s).replace(this.stripTagsRE, "");
22489     },
22490     
22491     /**
22492      * Strips all HTML tags to sort on text only - Case insensitive
22493      * @param {Mixed} s The value being converted
22494      * @return {String} The comparison value
22495      */
22496     asUCText : function(s){
22497         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22498     },
22499     
22500     /**
22501      * Case insensitive string
22502      * @param {Mixed} s The value being converted
22503      * @return {String} The comparison value
22504      */
22505     asUCString : function(s) {
22506         return String(s).toUpperCase();
22507     },
22508     
22509     /**
22510      * Date sorting
22511      * @param {Mixed} s The value being converted
22512      * @return {Number} The comparison value
22513      */
22514     asDate : function(s) {
22515         if(!s){
22516             return 0;
22517         }
22518         if(s instanceof Date){
22519             return s.getTime();
22520         }
22521         return Date.parse(String(s));
22522     },
22523     
22524     /**
22525      * Float sorting
22526      * @param {Mixed} s The value being converted
22527      * @return {Float} The comparison value
22528      */
22529     asFloat : function(s) {
22530         var val = parseFloat(String(s).replace(/,/g, ""));
22531         if(isNaN(val)) {
22532             val = 0;
22533         }
22534         return val;
22535     },
22536     
22537     /**
22538      * Integer sorting
22539      * @param {Mixed} s The value being converted
22540      * @return {Number} The comparison value
22541      */
22542     asInt : function(s) {
22543         var val = parseInt(String(s).replace(/,/g, ""));
22544         if(isNaN(val)) {
22545             val = 0;
22546         }
22547         return val;
22548     }
22549 };/*
22550  * Based on:
22551  * Ext JS Library 1.1.1
22552  * Copyright(c) 2006-2007, Ext JS, LLC.
22553  *
22554  * Originally Released Under LGPL - original licence link has changed is not relivant.
22555  *
22556  * Fork - LGPL
22557  * <script type="text/javascript">
22558  */
22559
22560 /**
22561 * @class Roo.data.Record
22562  * Instances of this class encapsulate both record <em>definition</em> information, and record
22563  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22564  * to access Records cached in an {@link Roo.data.Store} object.<br>
22565  * <p>
22566  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22567  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22568  * objects.<br>
22569  * <p>
22570  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22571  * @constructor
22572  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22573  * {@link #create}. The parameters are the same.
22574  * @param {Array} data An associative Array of data values keyed by the field name.
22575  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22576  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22577  * not specified an integer id is generated.
22578  */
22579 Roo.data.Record = function(data, id){
22580     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22581     this.data = data;
22582 };
22583
22584 /**
22585  * Generate a constructor for a specific record layout.
22586  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22587  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22588  * Each field definition object may contain the following properties: <ul>
22589  * <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,
22590  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22591  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22592  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22593  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22594  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22595  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22596  * this may be omitted.</p></li>
22597  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22598  * <ul><li>auto (Default, implies no conversion)</li>
22599  * <li>string</li>
22600  * <li>int</li>
22601  * <li>float</li>
22602  * <li>boolean</li>
22603  * <li>date</li></ul></p></li>
22604  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22605  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22606  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22607  * by the Reader into an object that will be stored in the Record. It is passed the
22608  * following parameters:<ul>
22609  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22610  * </ul></p></li>
22611  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22612  * </ul>
22613  * <br>usage:<br><pre><code>
22614 var TopicRecord = Roo.data.Record.create(
22615     {name: 'title', mapping: 'topic_title'},
22616     {name: 'author', mapping: 'username'},
22617     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22618     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22619     {name: 'lastPoster', mapping: 'user2'},
22620     {name: 'excerpt', mapping: 'post_text'}
22621 );
22622
22623 var myNewRecord = new TopicRecord({
22624     title: 'Do my job please',
22625     author: 'noobie',
22626     totalPosts: 1,
22627     lastPost: new Date(),
22628     lastPoster: 'Animal',
22629     excerpt: 'No way dude!'
22630 });
22631 myStore.add(myNewRecord);
22632 </code></pre>
22633  * @method create
22634  * @static
22635  */
22636 Roo.data.Record.create = function(o){
22637     var f = function(){
22638         f.superclass.constructor.apply(this, arguments);
22639     };
22640     Roo.extend(f, Roo.data.Record);
22641     var p = f.prototype;
22642     p.fields = new Roo.util.MixedCollection(false, function(field){
22643         return field.name;
22644     });
22645     for(var i = 0, len = o.length; i < len; i++){
22646         p.fields.add(new Roo.data.Field(o[i]));
22647     }
22648     f.getField = function(name){
22649         return p.fields.get(name);  
22650     };
22651     return f;
22652 };
22653
22654 Roo.data.Record.AUTO_ID = 1000;
22655 Roo.data.Record.EDIT = 'edit';
22656 Roo.data.Record.REJECT = 'reject';
22657 Roo.data.Record.COMMIT = 'commit';
22658
22659 Roo.data.Record.prototype = {
22660     /**
22661      * Readonly flag - true if this record has been modified.
22662      * @type Boolean
22663      */
22664     dirty : false,
22665     editing : false,
22666     error: null,
22667     modified: null,
22668
22669     // private
22670     join : function(store){
22671         this.store = store;
22672     },
22673
22674     /**
22675      * Set the named field to the specified value.
22676      * @param {String} name The name of the field to set.
22677      * @param {Object} value The value to set the field to.
22678      */
22679     set : function(name, value){
22680         if(this.data[name] == value){
22681             return;
22682         }
22683         this.dirty = true;
22684         if(!this.modified){
22685             this.modified = {};
22686         }
22687         if(typeof this.modified[name] == 'undefined'){
22688             this.modified[name] = this.data[name];
22689         }
22690         this.data[name] = value;
22691         if(!this.editing && this.store){
22692             this.store.afterEdit(this);
22693         }       
22694     },
22695
22696     /**
22697      * Get the value of the named field.
22698      * @param {String} name The name of the field to get the value of.
22699      * @return {Object} The value of the field.
22700      */
22701     get : function(name){
22702         return this.data[name]; 
22703     },
22704
22705     // private
22706     beginEdit : function(){
22707         this.editing = true;
22708         this.modified = {}; 
22709     },
22710
22711     // private
22712     cancelEdit : function(){
22713         this.editing = false;
22714         delete this.modified;
22715     },
22716
22717     // private
22718     endEdit : function(){
22719         this.editing = false;
22720         if(this.dirty && this.store){
22721             this.store.afterEdit(this);
22722         }
22723     },
22724
22725     /**
22726      * Usually called by the {@link Roo.data.Store} which owns the Record.
22727      * Rejects all changes made to the Record since either creation, or the last commit operation.
22728      * Modified fields are reverted to their original values.
22729      * <p>
22730      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22731      * of reject operations.
22732      */
22733     reject : function(){
22734         var m = this.modified;
22735         for(var n in m){
22736             if(typeof m[n] != "function"){
22737                 this.data[n] = m[n];
22738             }
22739         }
22740         this.dirty = false;
22741         delete this.modified;
22742         this.editing = false;
22743         if(this.store){
22744             this.store.afterReject(this);
22745         }
22746     },
22747
22748     /**
22749      * Usually called by the {@link Roo.data.Store} which owns the Record.
22750      * Commits all changes made to the Record since either creation, or the last commit operation.
22751      * <p>
22752      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22753      * of commit operations.
22754      */
22755     commit : function(){
22756         this.dirty = false;
22757         delete this.modified;
22758         this.editing = false;
22759         if(this.store){
22760             this.store.afterCommit(this);
22761         }
22762     },
22763
22764     // private
22765     hasError : function(){
22766         return this.error != null;
22767     },
22768
22769     // private
22770     clearError : function(){
22771         this.error = null;
22772     },
22773
22774     /**
22775      * Creates a copy of this record.
22776      * @param {String} id (optional) A new record id if you don't want to use this record's id
22777      * @return {Record}
22778      */
22779     copy : function(newId) {
22780         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22781     }
22782 };/*
22783  * Based on:
22784  * Ext JS Library 1.1.1
22785  * Copyright(c) 2006-2007, Ext JS, LLC.
22786  *
22787  * Originally Released Under LGPL - original licence link has changed is not relivant.
22788  *
22789  * Fork - LGPL
22790  * <script type="text/javascript">
22791  */
22792
22793
22794
22795 /**
22796  * @class Roo.data.Store
22797  * @extends Roo.util.Observable
22798  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22799  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22800  * <p>
22801  * 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
22802  * has no knowledge of the format of the data returned by the Proxy.<br>
22803  * <p>
22804  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22805  * instances from the data object. These records are cached and made available through accessor functions.
22806  * @constructor
22807  * Creates a new Store.
22808  * @param {Object} config A config object containing the objects needed for the Store to access data,
22809  * and read the data into Records.
22810  */
22811 Roo.data.Store = function(config){
22812     this.data = new Roo.util.MixedCollection(false);
22813     this.data.getKey = function(o){
22814         return o.id;
22815     };
22816     this.baseParams = {};
22817     // private
22818     this.paramNames = {
22819         "start" : "start",
22820         "limit" : "limit",
22821         "sort" : "sort",
22822         "dir" : "dir",
22823         "multisort" : "_multisort"
22824     };
22825
22826     if(config && config.data){
22827         this.inlineData = config.data;
22828         delete config.data;
22829     }
22830
22831     Roo.apply(this, config);
22832     
22833     if(this.reader){ // reader passed
22834         this.reader = Roo.factory(this.reader, Roo.data);
22835         this.reader.xmodule = this.xmodule || false;
22836         if(!this.recordType){
22837             this.recordType = this.reader.recordType;
22838         }
22839         if(this.reader.onMetaChange){
22840             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22841         }
22842     }
22843
22844     if(this.recordType){
22845         this.fields = this.recordType.prototype.fields;
22846     }
22847     this.modified = [];
22848
22849     this.addEvents({
22850         /**
22851          * @event datachanged
22852          * Fires when the data cache has changed, and a widget which is using this Store
22853          * as a Record cache should refresh its view.
22854          * @param {Store} this
22855          */
22856         datachanged : true,
22857         /**
22858          * @event metachange
22859          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22860          * @param {Store} this
22861          * @param {Object} meta The JSON metadata
22862          */
22863         metachange : true,
22864         /**
22865          * @event add
22866          * Fires when Records have been added to the Store
22867          * @param {Store} this
22868          * @param {Roo.data.Record[]} records The array of Records added
22869          * @param {Number} index The index at which the record(s) were added
22870          */
22871         add : true,
22872         /**
22873          * @event remove
22874          * Fires when a Record has been removed from the Store
22875          * @param {Store} this
22876          * @param {Roo.data.Record} record The Record that was removed
22877          * @param {Number} index The index at which the record was removed
22878          */
22879         remove : true,
22880         /**
22881          * @event update
22882          * Fires when a Record has been updated
22883          * @param {Store} this
22884          * @param {Roo.data.Record} record The Record that was updated
22885          * @param {String} operation The update operation being performed.  Value may be one of:
22886          * <pre><code>
22887  Roo.data.Record.EDIT
22888  Roo.data.Record.REJECT
22889  Roo.data.Record.COMMIT
22890          * </code></pre>
22891          */
22892         update : true,
22893         /**
22894          * @event clear
22895          * Fires when the data cache has been cleared.
22896          * @param {Store} this
22897          */
22898         clear : true,
22899         /**
22900          * @event beforeload
22901          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22902          * the load action will be canceled.
22903          * @param {Store} this
22904          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22905          */
22906         beforeload : true,
22907         /**
22908          * @event beforeloadadd
22909          * Fires after a new set of Records has been loaded.
22910          * @param {Store} this
22911          * @param {Roo.data.Record[]} records The Records that were loaded
22912          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22913          */
22914         beforeloadadd : true,
22915         /**
22916          * @event load
22917          * Fires after a new set of Records has been loaded, before they are added to the store.
22918          * @param {Store} this
22919          * @param {Roo.data.Record[]} records The Records that were loaded
22920          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22921          * @params {Object} return from reader
22922          */
22923         load : true,
22924         /**
22925          * @event loadexception
22926          * Fires if an exception occurs in the Proxy during loading.
22927          * Called with the signature of the Proxy's "loadexception" event.
22928          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22929          * 
22930          * @param {Proxy} 
22931          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22932          * @param {Object} load options 
22933          * @param {Object} jsonData from your request (normally this contains the Exception)
22934          */
22935         loadexception : true
22936     });
22937     
22938     if(this.proxy){
22939         this.proxy = Roo.factory(this.proxy, Roo.data);
22940         this.proxy.xmodule = this.xmodule || false;
22941         this.relayEvents(this.proxy,  ["loadexception"]);
22942     }
22943     this.sortToggle = {};
22944     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22945
22946     Roo.data.Store.superclass.constructor.call(this);
22947
22948     if(this.inlineData){
22949         this.loadData(this.inlineData);
22950         delete this.inlineData;
22951     }
22952 };
22953
22954 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22955      /**
22956     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22957     * without a remote query - used by combo/forms at present.
22958     */
22959     
22960     /**
22961     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22962     */
22963     /**
22964     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22965     */
22966     /**
22967     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22968     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22969     */
22970     /**
22971     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22972     * on any HTTP request
22973     */
22974     /**
22975     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22976     */
22977     /**
22978     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22979     */
22980     multiSort: false,
22981     /**
22982     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22983     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22984     */
22985     remoteSort : false,
22986
22987     /**
22988     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22989      * loaded or when a record is removed. (defaults to false).
22990     */
22991     pruneModifiedRecords : false,
22992
22993     // private
22994     lastOptions : null,
22995
22996     /**
22997      * Add Records to the Store and fires the add event.
22998      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22999      */
23000     add : function(records){
23001         records = [].concat(records);
23002         for(var i = 0, len = records.length; i < len; i++){
23003             records[i].join(this);
23004         }
23005         var index = this.data.length;
23006         this.data.addAll(records);
23007         this.fireEvent("add", this, records, index);
23008     },
23009
23010     /**
23011      * Remove a Record from the Store and fires the remove event.
23012      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23013      */
23014     remove : function(record){
23015         var index = this.data.indexOf(record);
23016         this.data.removeAt(index);
23017  
23018         if(this.pruneModifiedRecords){
23019             this.modified.remove(record);
23020         }
23021         this.fireEvent("remove", this, record, index);
23022     },
23023
23024     /**
23025      * Remove all Records from the Store and fires the clear event.
23026      */
23027     removeAll : function(){
23028         this.data.clear();
23029         if(this.pruneModifiedRecords){
23030             this.modified = [];
23031         }
23032         this.fireEvent("clear", this);
23033     },
23034
23035     /**
23036      * Inserts Records to the Store at the given index and fires the add event.
23037      * @param {Number} index The start index at which to insert the passed Records.
23038      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23039      */
23040     insert : function(index, records){
23041         records = [].concat(records);
23042         for(var i = 0, len = records.length; i < len; i++){
23043             this.data.insert(index, records[i]);
23044             records[i].join(this);
23045         }
23046         this.fireEvent("add", this, records, index);
23047     },
23048
23049     /**
23050      * Get the index within the cache of the passed Record.
23051      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23052      * @return {Number} The index of the passed Record. Returns -1 if not found.
23053      */
23054     indexOf : function(record){
23055         return this.data.indexOf(record);
23056     },
23057
23058     /**
23059      * Get the index within the cache of the Record with the passed id.
23060      * @param {String} id The id of the Record to find.
23061      * @return {Number} The index of the Record. Returns -1 if not found.
23062      */
23063     indexOfId : function(id){
23064         return this.data.indexOfKey(id);
23065     },
23066
23067     /**
23068      * Get the Record with the specified id.
23069      * @param {String} id The id of the Record to find.
23070      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23071      */
23072     getById : function(id){
23073         return this.data.key(id);
23074     },
23075
23076     /**
23077      * Get the Record at the specified index.
23078      * @param {Number} index The index of the Record to find.
23079      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23080      */
23081     getAt : function(index){
23082         return this.data.itemAt(index);
23083     },
23084
23085     /**
23086      * Returns a range of Records between specified indices.
23087      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23088      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23089      * @return {Roo.data.Record[]} An array of Records
23090      */
23091     getRange : function(start, end){
23092         return this.data.getRange(start, end);
23093     },
23094
23095     // private
23096     storeOptions : function(o){
23097         o = Roo.apply({}, o);
23098         delete o.callback;
23099         delete o.scope;
23100         this.lastOptions = o;
23101     },
23102
23103     /**
23104      * Loads the Record cache from the configured Proxy using the configured Reader.
23105      * <p>
23106      * If using remote paging, then the first load call must specify the <em>start</em>
23107      * and <em>limit</em> properties in the options.params property to establish the initial
23108      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23109      * <p>
23110      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23111      * and this call will return before the new data has been loaded. Perform any post-processing
23112      * in a callback function, or in a "load" event handler.</strong>
23113      * <p>
23114      * @param {Object} options An object containing properties which control loading options:<ul>
23115      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23116      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23117      * passed the following arguments:<ul>
23118      * <li>r : Roo.data.Record[]</li>
23119      * <li>options: Options object from the load call</li>
23120      * <li>success: Boolean success indicator</li></ul></li>
23121      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23122      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23123      * </ul>
23124      */
23125     load : function(options){
23126         options = options || {};
23127         if(this.fireEvent("beforeload", this, options) !== false){
23128             this.storeOptions(options);
23129             var p = Roo.apply(options.params || {}, this.baseParams);
23130             // if meta was not loaded from remote source.. try requesting it.
23131             if (!this.reader.metaFromRemote) {
23132                 p._requestMeta = 1;
23133             }
23134             if(this.sortInfo && this.remoteSort){
23135                 var pn = this.paramNames;
23136                 p[pn["sort"]] = this.sortInfo.field;
23137                 p[pn["dir"]] = this.sortInfo.direction;
23138             }
23139             if (this.multiSort) {
23140                 var pn = this.paramNames;
23141                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23142             }
23143             
23144             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23145         }
23146     },
23147
23148     /**
23149      * Reloads the Record cache from the configured Proxy using the configured Reader and
23150      * the options from the last load operation performed.
23151      * @param {Object} options (optional) An object containing properties which may override the options
23152      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23153      * the most recently used options are reused).
23154      */
23155     reload : function(options){
23156         this.load(Roo.applyIf(options||{}, this.lastOptions));
23157     },
23158
23159     // private
23160     // Called as a callback by the Reader during a load operation.
23161     loadRecords : function(o, options, success){
23162         if(!o || success === false){
23163             if(success !== false){
23164                 this.fireEvent("load", this, [], options, o);
23165             }
23166             if(options.callback){
23167                 options.callback.call(options.scope || this, [], options, false);
23168             }
23169             return;
23170         }
23171         // if data returned failure - throw an exception.
23172         if (o.success === false) {
23173             // show a message if no listener is registered.
23174             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23175                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23176             }
23177             // loadmask wil be hooked into this..
23178             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23179             return;
23180         }
23181         var r = o.records, t = o.totalRecords || r.length;
23182         
23183         this.fireEvent("beforeloadadd", this, r, options, o);
23184         
23185         if(!options || options.add !== true){
23186             if(this.pruneModifiedRecords){
23187                 this.modified = [];
23188             }
23189             for(var i = 0, len = r.length; i < len; i++){
23190                 r[i].join(this);
23191             }
23192             if(this.snapshot){
23193                 this.data = this.snapshot;
23194                 delete this.snapshot;
23195             }
23196             this.data.clear();
23197             this.data.addAll(r);
23198             this.totalLength = t;
23199             this.applySort();
23200             this.fireEvent("datachanged", this);
23201         }else{
23202             this.totalLength = Math.max(t, this.data.length+r.length);
23203             this.add(r);
23204         }
23205         
23206         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23207                 
23208             var e = new Roo.data.Record({});
23209
23210             e.set(this.parent.displayField, this.parent.emptyTitle);
23211             e.set(this.parent.valueField, '');
23212
23213             this.insert(0, e);
23214         }
23215             
23216         this.fireEvent("load", this, r, options, o);
23217         if(options.callback){
23218             options.callback.call(options.scope || this, r, options, true);
23219         }
23220     },
23221
23222
23223     /**
23224      * Loads data from a passed data block. A Reader which understands the format of the data
23225      * must have been configured in the constructor.
23226      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23227      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23228      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23229      */
23230     loadData : function(o, append){
23231         var r = this.reader.readRecords(o);
23232         this.loadRecords(r, {add: append}, true);
23233     },
23234
23235     /**
23236      * Gets the number of cached records.
23237      * <p>
23238      * <em>If using paging, this may not be the total size of the dataset. If the data object
23239      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23240      * the data set size</em>
23241      */
23242     getCount : function(){
23243         return this.data.length || 0;
23244     },
23245
23246     /**
23247      * Gets the total number of records in the dataset as returned by the server.
23248      * <p>
23249      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23250      * the dataset size</em>
23251      */
23252     getTotalCount : function(){
23253         return this.totalLength || 0;
23254     },
23255
23256     /**
23257      * Returns the sort state of the Store as an object with two properties:
23258      * <pre><code>
23259  field {String} The name of the field by which the Records are sorted
23260  direction {String} The sort order, "ASC" or "DESC"
23261      * </code></pre>
23262      */
23263     getSortState : function(){
23264         return this.sortInfo;
23265     },
23266
23267     // private
23268     applySort : function(){
23269         if(this.sortInfo && !this.remoteSort){
23270             var s = this.sortInfo, f = s.field;
23271             var st = this.fields.get(f).sortType;
23272             var fn = function(r1, r2){
23273                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23274                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23275             };
23276             this.data.sort(s.direction, fn);
23277             if(this.snapshot && this.snapshot != this.data){
23278                 this.snapshot.sort(s.direction, fn);
23279             }
23280         }
23281     },
23282
23283     /**
23284      * Sets the default sort column and order to be used by the next load operation.
23285      * @param {String} fieldName The name of the field to sort by.
23286      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23287      */
23288     setDefaultSort : function(field, dir){
23289         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23290     },
23291
23292     /**
23293      * Sort the Records.
23294      * If remote sorting is used, the sort is performed on the server, and the cache is
23295      * reloaded. If local sorting is used, the cache is sorted internally.
23296      * @param {String} fieldName The name of the field to sort by.
23297      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23298      */
23299     sort : function(fieldName, dir){
23300         var f = this.fields.get(fieldName);
23301         if(!dir){
23302             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23303             
23304             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23305                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23306             }else{
23307                 dir = f.sortDir;
23308             }
23309         }
23310         this.sortToggle[f.name] = dir;
23311         this.sortInfo = {field: f.name, direction: dir};
23312         if(!this.remoteSort){
23313             this.applySort();
23314             this.fireEvent("datachanged", this);
23315         }else{
23316             this.load(this.lastOptions);
23317         }
23318     },
23319
23320     /**
23321      * Calls the specified function for each of the Records in the cache.
23322      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23323      * Returning <em>false</em> aborts and exits the iteration.
23324      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23325      */
23326     each : function(fn, scope){
23327         this.data.each(fn, scope);
23328     },
23329
23330     /**
23331      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23332      * (e.g., during paging).
23333      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23334      */
23335     getModifiedRecords : function(){
23336         return this.modified;
23337     },
23338
23339     // private
23340     createFilterFn : function(property, value, anyMatch){
23341         if(!value.exec){ // not a regex
23342             value = String(value);
23343             if(value.length == 0){
23344                 return false;
23345             }
23346             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23347         }
23348         return function(r){
23349             return value.test(r.data[property]);
23350         };
23351     },
23352
23353     /**
23354      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23355      * @param {String} property A field on your records
23356      * @param {Number} start The record index to start at (defaults to 0)
23357      * @param {Number} end The last record index to include (defaults to length - 1)
23358      * @return {Number} The sum
23359      */
23360     sum : function(property, start, end){
23361         var rs = this.data.items, v = 0;
23362         start = start || 0;
23363         end = (end || end === 0) ? end : rs.length-1;
23364
23365         for(var i = start; i <= end; i++){
23366             v += (rs[i].data[property] || 0);
23367         }
23368         return v;
23369     },
23370
23371     /**
23372      * Filter the records by a specified property.
23373      * @param {String} field A field on your records
23374      * @param {String/RegExp} value Either a string that the field
23375      * should start with or a RegExp to test against the field
23376      * @param {Boolean} anyMatch True to match any part not just the beginning
23377      */
23378     filter : function(property, value, anyMatch){
23379         var fn = this.createFilterFn(property, value, anyMatch);
23380         return fn ? this.filterBy(fn) : this.clearFilter();
23381     },
23382
23383     /**
23384      * Filter by a function. The specified function will be called with each
23385      * record in this data source. If the function returns true the record is included,
23386      * otherwise it is filtered.
23387      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23388      * @param {Object} scope (optional) The scope of the function (defaults to this)
23389      */
23390     filterBy : function(fn, scope){
23391         this.snapshot = this.snapshot || this.data;
23392         this.data = this.queryBy(fn, scope||this);
23393         this.fireEvent("datachanged", this);
23394     },
23395
23396     /**
23397      * Query the records by a specified property.
23398      * @param {String} field A field on your records
23399      * @param {String/RegExp} value Either a string that the field
23400      * should start with or a RegExp to test against the field
23401      * @param {Boolean} anyMatch True to match any part not just the beginning
23402      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23403      */
23404     query : function(property, value, anyMatch){
23405         var fn = this.createFilterFn(property, value, anyMatch);
23406         return fn ? this.queryBy(fn) : this.data.clone();
23407     },
23408
23409     /**
23410      * Query by a function. The specified function will be called with each
23411      * record in this data source. If the function returns true the record is included
23412      * in the results.
23413      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23414      * @param {Object} scope (optional) The scope of the function (defaults to this)
23415       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23416      **/
23417     queryBy : function(fn, scope){
23418         var data = this.snapshot || this.data;
23419         return data.filterBy(fn, scope||this);
23420     },
23421
23422     /**
23423      * Collects unique values for a particular dataIndex from this store.
23424      * @param {String} dataIndex The property to collect
23425      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23426      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23427      * @return {Array} An array of the unique values
23428      **/
23429     collect : function(dataIndex, allowNull, bypassFilter){
23430         var d = (bypassFilter === true && this.snapshot) ?
23431                 this.snapshot.items : this.data.items;
23432         var v, sv, r = [], l = {};
23433         for(var i = 0, len = d.length; i < len; i++){
23434             v = d[i].data[dataIndex];
23435             sv = String(v);
23436             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23437                 l[sv] = true;
23438                 r[r.length] = v;
23439             }
23440         }
23441         return r;
23442     },
23443
23444     /**
23445      * Revert to a view of the Record cache with no filtering applied.
23446      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23447      */
23448     clearFilter : function(suppressEvent){
23449         if(this.snapshot && this.snapshot != this.data){
23450             this.data = this.snapshot;
23451             delete this.snapshot;
23452             if(suppressEvent !== true){
23453                 this.fireEvent("datachanged", this);
23454             }
23455         }
23456     },
23457
23458     // private
23459     afterEdit : function(record){
23460         if(this.modified.indexOf(record) == -1){
23461             this.modified.push(record);
23462         }
23463         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23464     },
23465     
23466     // private
23467     afterReject : function(record){
23468         this.modified.remove(record);
23469         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23470     },
23471
23472     // private
23473     afterCommit : function(record){
23474         this.modified.remove(record);
23475         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23476     },
23477
23478     /**
23479      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23480      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23481      */
23482     commitChanges : function(){
23483         var m = this.modified.slice(0);
23484         this.modified = [];
23485         for(var i = 0, len = m.length; i < len; i++){
23486             m[i].commit();
23487         }
23488     },
23489
23490     /**
23491      * Cancel outstanding changes on all changed records.
23492      */
23493     rejectChanges : function(){
23494         var m = this.modified.slice(0);
23495         this.modified = [];
23496         for(var i = 0, len = m.length; i < len; i++){
23497             m[i].reject();
23498         }
23499     },
23500
23501     onMetaChange : function(meta, rtype, o){
23502         this.recordType = rtype;
23503         this.fields = rtype.prototype.fields;
23504         delete this.snapshot;
23505         this.sortInfo = meta.sortInfo || this.sortInfo;
23506         this.modified = [];
23507         this.fireEvent('metachange', this, this.reader.meta);
23508     },
23509     
23510     moveIndex : function(data, type)
23511     {
23512         var index = this.indexOf(data);
23513         
23514         var newIndex = index + type;
23515         
23516         this.remove(data);
23517         
23518         this.insert(newIndex, data);
23519         
23520     }
23521 });/*
23522  * Based on:
23523  * Ext JS Library 1.1.1
23524  * Copyright(c) 2006-2007, Ext JS, LLC.
23525  *
23526  * Originally Released Under LGPL - original licence link has changed is not relivant.
23527  *
23528  * Fork - LGPL
23529  * <script type="text/javascript">
23530  */
23531
23532 /**
23533  * @class Roo.data.SimpleStore
23534  * @extends Roo.data.Store
23535  * Small helper class to make creating Stores from Array data easier.
23536  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23537  * @cfg {Array} fields An array of field definition objects, or field name strings.
23538  * @cfg {Array} data The multi-dimensional array of data
23539  * @constructor
23540  * @param {Object} config
23541  */
23542 Roo.data.SimpleStore = function(config){
23543     Roo.data.SimpleStore.superclass.constructor.call(this, {
23544         isLocal : true,
23545         reader: new Roo.data.ArrayReader({
23546                 id: config.id
23547             },
23548             Roo.data.Record.create(config.fields)
23549         ),
23550         proxy : new Roo.data.MemoryProxy(config.data)
23551     });
23552     this.load();
23553 };
23554 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23555  * Based on:
23556  * Ext JS Library 1.1.1
23557  * Copyright(c) 2006-2007, Ext JS, LLC.
23558  *
23559  * Originally Released Under LGPL - original licence link has changed is not relivant.
23560  *
23561  * Fork - LGPL
23562  * <script type="text/javascript">
23563  */
23564
23565 /**
23566 /**
23567  * @extends Roo.data.Store
23568  * @class Roo.data.JsonStore
23569  * Small helper class to make creating Stores for JSON data easier. <br/>
23570 <pre><code>
23571 var store = new Roo.data.JsonStore({
23572     url: 'get-images.php',
23573     root: 'images',
23574     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23575 });
23576 </code></pre>
23577  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23578  * JsonReader and HttpProxy (unless inline data is provided).</b>
23579  * @cfg {Array} fields An array of field definition objects, or field name strings.
23580  * @constructor
23581  * @param {Object} config
23582  */
23583 Roo.data.JsonStore = function(c){
23584     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23585         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23586         reader: new Roo.data.JsonReader(c, c.fields)
23587     }));
23588 };
23589 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23590  * Based on:
23591  * Ext JS Library 1.1.1
23592  * Copyright(c) 2006-2007, Ext JS, LLC.
23593  *
23594  * Originally Released Under LGPL - original licence link has changed is not relivant.
23595  *
23596  * Fork - LGPL
23597  * <script type="text/javascript">
23598  */
23599
23600  
23601 Roo.data.Field = function(config){
23602     if(typeof config == "string"){
23603         config = {name: config};
23604     }
23605     Roo.apply(this, config);
23606     
23607     if(!this.type){
23608         this.type = "auto";
23609     }
23610     
23611     var st = Roo.data.SortTypes;
23612     // named sortTypes are supported, here we look them up
23613     if(typeof this.sortType == "string"){
23614         this.sortType = st[this.sortType];
23615     }
23616     
23617     // set default sortType for strings and dates
23618     if(!this.sortType){
23619         switch(this.type){
23620             case "string":
23621                 this.sortType = st.asUCString;
23622                 break;
23623             case "date":
23624                 this.sortType = st.asDate;
23625                 break;
23626             default:
23627                 this.sortType = st.none;
23628         }
23629     }
23630
23631     // define once
23632     var stripRe = /[\$,%]/g;
23633
23634     // prebuilt conversion function for this field, instead of
23635     // switching every time we're reading a value
23636     if(!this.convert){
23637         var cv, dateFormat = this.dateFormat;
23638         switch(this.type){
23639             case "":
23640             case "auto":
23641             case undefined:
23642                 cv = function(v){ return v; };
23643                 break;
23644             case "string":
23645                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23646                 break;
23647             case "int":
23648                 cv = function(v){
23649                     return v !== undefined && v !== null && v !== '' ?
23650                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23651                     };
23652                 break;
23653             case "float":
23654                 cv = function(v){
23655                     return v !== undefined && v !== null && v !== '' ?
23656                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23657                     };
23658                 break;
23659             case "bool":
23660             case "boolean":
23661                 cv = function(v){ return v === true || v === "true" || v == 1; };
23662                 break;
23663             case "date":
23664                 cv = function(v){
23665                     if(!v){
23666                         return '';
23667                     }
23668                     if(v instanceof Date){
23669                         return v;
23670                     }
23671                     if(dateFormat){
23672                         if(dateFormat == "timestamp"){
23673                             return new Date(v*1000);
23674                         }
23675                         return Date.parseDate(v, dateFormat);
23676                     }
23677                     var parsed = Date.parse(v);
23678                     return parsed ? new Date(parsed) : null;
23679                 };
23680              break;
23681             
23682         }
23683         this.convert = cv;
23684     }
23685 };
23686
23687 Roo.data.Field.prototype = {
23688     dateFormat: null,
23689     defaultValue: "",
23690     mapping: null,
23691     sortType : null,
23692     sortDir : "ASC"
23693 };/*
23694  * Based on:
23695  * Ext JS Library 1.1.1
23696  * Copyright(c) 2006-2007, Ext JS, LLC.
23697  *
23698  * Originally Released Under LGPL - original licence link has changed is not relivant.
23699  *
23700  * Fork - LGPL
23701  * <script type="text/javascript">
23702  */
23703  
23704 // Base class for reading structured data from a data source.  This class is intended to be
23705 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23706
23707 /**
23708  * @class Roo.data.DataReader
23709  * Base class for reading structured data from a data source.  This class is intended to be
23710  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23711  */
23712
23713 Roo.data.DataReader = function(meta, recordType){
23714     
23715     this.meta = meta;
23716     
23717     this.recordType = recordType instanceof Array ? 
23718         Roo.data.Record.create(recordType) : recordType;
23719 };
23720
23721 Roo.data.DataReader.prototype = {
23722      /**
23723      * Create an empty record
23724      * @param {Object} data (optional) - overlay some values
23725      * @return {Roo.data.Record} record created.
23726      */
23727     newRow :  function(d) {
23728         var da =  {};
23729         this.recordType.prototype.fields.each(function(c) {
23730             switch( c.type) {
23731                 case 'int' : da[c.name] = 0; break;
23732                 case 'date' : da[c.name] = new Date(); break;
23733                 case 'float' : da[c.name] = 0.0; break;
23734                 case 'boolean' : da[c.name] = false; break;
23735                 default : da[c.name] = ""; break;
23736             }
23737             
23738         });
23739         return new this.recordType(Roo.apply(da, d));
23740     }
23741     
23742 };/*
23743  * Based on:
23744  * Ext JS Library 1.1.1
23745  * Copyright(c) 2006-2007, Ext JS, LLC.
23746  *
23747  * Originally Released Under LGPL - original licence link has changed is not relivant.
23748  *
23749  * Fork - LGPL
23750  * <script type="text/javascript">
23751  */
23752
23753 /**
23754  * @class Roo.data.DataProxy
23755  * @extends Roo.data.Observable
23756  * This class is an abstract base class for implementations which provide retrieval of
23757  * unformatted data objects.<br>
23758  * <p>
23759  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23760  * (of the appropriate type which knows how to parse the data object) to provide a block of
23761  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23762  * <p>
23763  * Custom implementations must implement the load method as described in
23764  * {@link Roo.data.HttpProxy#load}.
23765  */
23766 Roo.data.DataProxy = function(){
23767     this.addEvents({
23768         /**
23769          * @event beforeload
23770          * Fires before a network request is made to retrieve a data object.
23771          * @param {Object} This DataProxy object.
23772          * @param {Object} params The params parameter to the load function.
23773          */
23774         beforeload : true,
23775         /**
23776          * @event load
23777          * Fires before the load method's callback is called.
23778          * @param {Object} This DataProxy object.
23779          * @param {Object} o The data object.
23780          * @param {Object} arg The callback argument object passed to the load function.
23781          */
23782         load : true,
23783         /**
23784          * @event loadexception
23785          * Fires if an Exception occurs during data retrieval.
23786          * @param {Object} This DataProxy object.
23787          * @param {Object} o The data object.
23788          * @param {Object} arg The callback argument object passed to the load function.
23789          * @param {Object} e The Exception.
23790          */
23791         loadexception : true
23792     });
23793     Roo.data.DataProxy.superclass.constructor.call(this);
23794 };
23795
23796 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23797
23798     /**
23799      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23800      */
23801 /*
23802  * Based on:
23803  * Ext JS Library 1.1.1
23804  * Copyright(c) 2006-2007, Ext JS, LLC.
23805  *
23806  * Originally Released Under LGPL - original licence link has changed is not relivant.
23807  *
23808  * Fork - LGPL
23809  * <script type="text/javascript">
23810  */
23811 /**
23812  * @class Roo.data.MemoryProxy
23813  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23814  * to the Reader when its load method is called.
23815  * @constructor
23816  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23817  */
23818 Roo.data.MemoryProxy = function(data){
23819     if (data.data) {
23820         data = data.data;
23821     }
23822     Roo.data.MemoryProxy.superclass.constructor.call(this);
23823     this.data = data;
23824 };
23825
23826 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23827     
23828     /**
23829      * Load data from the requested source (in this case an in-memory
23830      * data object passed to the constructor), read the data object into
23831      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23832      * process that block using the passed callback.
23833      * @param {Object} params This parameter is not used by the MemoryProxy class.
23834      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23835      * object into a block of Roo.data.Records.
23836      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23837      * The function must be passed <ul>
23838      * <li>The Record block object</li>
23839      * <li>The "arg" argument from the load function</li>
23840      * <li>A boolean success indicator</li>
23841      * </ul>
23842      * @param {Object} scope The scope in which to call the callback
23843      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23844      */
23845     load : function(params, reader, callback, scope, arg){
23846         params = params || {};
23847         var result;
23848         try {
23849             result = reader.readRecords(this.data);
23850         }catch(e){
23851             this.fireEvent("loadexception", this, arg, null, e);
23852             callback.call(scope, null, arg, false);
23853             return;
23854         }
23855         callback.call(scope, result, arg, true);
23856     },
23857     
23858     // private
23859     update : function(params, records){
23860         
23861     }
23862 });/*
23863  * Based on:
23864  * Ext JS Library 1.1.1
23865  * Copyright(c) 2006-2007, Ext JS, LLC.
23866  *
23867  * Originally Released Under LGPL - original licence link has changed is not relivant.
23868  *
23869  * Fork - LGPL
23870  * <script type="text/javascript">
23871  */
23872 /**
23873  * @class Roo.data.HttpProxy
23874  * @extends Roo.data.DataProxy
23875  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23876  * configured to reference a certain URL.<br><br>
23877  * <p>
23878  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23879  * from which the running page was served.<br><br>
23880  * <p>
23881  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23882  * <p>
23883  * Be aware that to enable the browser to parse an XML document, the server must set
23884  * the Content-Type header in the HTTP response to "text/xml".
23885  * @constructor
23886  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23887  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23888  * will be used to make the request.
23889  */
23890 Roo.data.HttpProxy = function(conn){
23891     Roo.data.HttpProxy.superclass.constructor.call(this);
23892     // is conn a conn config or a real conn?
23893     this.conn = conn;
23894     this.useAjax = !conn || !conn.events;
23895   
23896 };
23897
23898 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23899     // thse are take from connection...
23900     
23901     /**
23902      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23903      */
23904     /**
23905      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23906      * extra parameters to each request made by this object. (defaults to undefined)
23907      */
23908     /**
23909      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23910      *  to each request made by this object. (defaults to undefined)
23911      */
23912     /**
23913      * @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)
23914      */
23915     /**
23916      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23917      */
23918      /**
23919      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23920      * @type Boolean
23921      */
23922   
23923
23924     /**
23925      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23926      * @type Boolean
23927      */
23928     /**
23929      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23930      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23931      * a finer-grained basis than the DataProxy events.
23932      */
23933     getConnection : function(){
23934         return this.useAjax ? Roo.Ajax : this.conn;
23935     },
23936
23937     /**
23938      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23939      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23940      * process that block using the passed callback.
23941      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23942      * for the request to the remote server.
23943      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23944      * object into a block of Roo.data.Records.
23945      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23946      * The function must be passed <ul>
23947      * <li>The Record block object</li>
23948      * <li>The "arg" argument from the load function</li>
23949      * <li>A boolean success indicator</li>
23950      * </ul>
23951      * @param {Object} scope The scope in which to call the callback
23952      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23953      */
23954     load : function(params, reader, callback, scope, arg){
23955         if(this.fireEvent("beforeload", this, params) !== false){
23956             var  o = {
23957                 params : params || {},
23958                 request: {
23959                     callback : callback,
23960                     scope : scope,
23961                     arg : arg
23962                 },
23963                 reader: reader,
23964                 callback : this.loadResponse,
23965                 scope: this
23966             };
23967             if(this.useAjax){
23968                 Roo.applyIf(o, this.conn);
23969                 if(this.activeRequest){
23970                     Roo.Ajax.abort(this.activeRequest);
23971                 }
23972                 this.activeRequest = Roo.Ajax.request(o);
23973             }else{
23974                 this.conn.request(o);
23975             }
23976         }else{
23977             callback.call(scope||this, null, arg, false);
23978         }
23979     },
23980
23981     // private
23982     loadResponse : function(o, success, response){
23983         delete this.activeRequest;
23984         if(!success){
23985             this.fireEvent("loadexception", this, o, response);
23986             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23987             return;
23988         }
23989         var result;
23990         try {
23991             result = o.reader.read(response);
23992         }catch(e){
23993             this.fireEvent("loadexception", this, o, response, e);
23994             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23995             return;
23996         }
23997         
23998         this.fireEvent("load", this, o, o.request.arg);
23999         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24000     },
24001
24002     // private
24003     update : function(dataSet){
24004
24005     },
24006
24007     // private
24008     updateResponse : function(dataSet){
24009
24010     }
24011 });/*
24012  * Based on:
24013  * Ext JS Library 1.1.1
24014  * Copyright(c) 2006-2007, Ext JS, LLC.
24015  *
24016  * Originally Released Under LGPL - original licence link has changed is not relivant.
24017  *
24018  * Fork - LGPL
24019  * <script type="text/javascript">
24020  */
24021
24022 /**
24023  * @class Roo.data.ScriptTagProxy
24024  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24025  * other than the originating domain of the running page.<br><br>
24026  * <p>
24027  * <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
24028  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24029  * <p>
24030  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24031  * source code that is used as the source inside a &lt;script> tag.<br><br>
24032  * <p>
24033  * In order for the browser to process the returned data, the server must wrap the data object
24034  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24035  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24036  * depending on whether the callback name was passed:
24037  * <p>
24038  * <pre><code>
24039 boolean scriptTag = false;
24040 String cb = request.getParameter("callback");
24041 if (cb != null) {
24042     scriptTag = true;
24043     response.setContentType("text/javascript");
24044 } else {
24045     response.setContentType("application/x-json");
24046 }
24047 Writer out = response.getWriter();
24048 if (scriptTag) {
24049     out.write(cb + "(");
24050 }
24051 out.print(dataBlock.toJsonString());
24052 if (scriptTag) {
24053     out.write(");");
24054 }
24055 </pre></code>
24056  *
24057  * @constructor
24058  * @param {Object} config A configuration object.
24059  */
24060 Roo.data.ScriptTagProxy = function(config){
24061     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24062     Roo.apply(this, config);
24063     this.head = document.getElementsByTagName("head")[0];
24064 };
24065
24066 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24067
24068 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24069     /**
24070      * @cfg {String} url The URL from which to request the data object.
24071      */
24072     /**
24073      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24074      */
24075     timeout : 30000,
24076     /**
24077      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24078      * the server the name of the callback function set up by the load call to process the returned data object.
24079      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24080      * javascript output which calls this named function passing the data object as its only parameter.
24081      */
24082     callbackParam : "callback",
24083     /**
24084      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24085      * name to the request.
24086      */
24087     nocache : true,
24088
24089     /**
24090      * Load data from the configured URL, read the data object into
24091      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24092      * process that block using the passed callback.
24093      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24094      * for the request to the remote server.
24095      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24096      * object into a block of Roo.data.Records.
24097      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24098      * The function must be passed <ul>
24099      * <li>The Record block object</li>
24100      * <li>The "arg" argument from the load function</li>
24101      * <li>A boolean success indicator</li>
24102      * </ul>
24103      * @param {Object} scope The scope in which to call the callback
24104      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24105      */
24106     load : function(params, reader, callback, scope, arg){
24107         if(this.fireEvent("beforeload", this, params) !== false){
24108
24109             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24110
24111             var url = this.url;
24112             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24113             if(this.nocache){
24114                 url += "&_dc=" + (new Date().getTime());
24115             }
24116             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24117             var trans = {
24118                 id : transId,
24119                 cb : "stcCallback"+transId,
24120                 scriptId : "stcScript"+transId,
24121                 params : params,
24122                 arg : arg,
24123                 url : url,
24124                 callback : callback,
24125                 scope : scope,
24126                 reader : reader
24127             };
24128             var conn = this;
24129
24130             window[trans.cb] = function(o){
24131                 conn.handleResponse(o, trans);
24132             };
24133
24134             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24135
24136             if(this.autoAbort !== false){
24137                 this.abort();
24138             }
24139
24140             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24141
24142             var script = document.createElement("script");
24143             script.setAttribute("src", url);
24144             script.setAttribute("type", "text/javascript");
24145             script.setAttribute("id", trans.scriptId);
24146             this.head.appendChild(script);
24147
24148             this.trans = trans;
24149         }else{
24150             callback.call(scope||this, null, arg, false);
24151         }
24152     },
24153
24154     // private
24155     isLoading : function(){
24156         return this.trans ? true : false;
24157     },
24158
24159     /**
24160      * Abort the current server request.
24161      */
24162     abort : function(){
24163         if(this.isLoading()){
24164             this.destroyTrans(this.trans);
24165         }
24166     },
24167
24168     // private
24169     destroyTrans : function(trans, isLoaded){
24170         this.head.removeChild(document.getElementById(trans.scriptId));
24171         clearTimeout(trans.timeoutId);
24172         if(isLoaded){
24173             window[trans.cb] = undefined;
24174             try{
24175                 delete window[trans.cb];
24176             }catch(e){}
24177         }else{
24178             // if hasn't been loaded, wait for load to remove it to prevent script error
24179             window[trans.cb] = function(){
24180                 window[trans.cb] = undefined;
24181                 try{
24182                     delete window[trans.cb];
24183                 }catch(e){}
24184             };
24185         }
24186     },
24187
24188     // private
24189     handleResponse : function(o, trans){
24190         this.trans = false;
24191         this.destroyTrans(trans, true);
24192         var result;
24193         try {
24194             result = trans.reader.readRecords(o);
24195         }catch(e){
24196             this.fireEvent("loadexception", this, o, trans.arg, e);
24197             trans.callback.call(trans.scope||window, null, trans.arg, false);
24198             return;
24199         }
24200         this.fireEvent("load", this, o, trans.arg);
24201         trans.callback.call(trans.scope||window, result, trans.arg, true);
24202     },
24203
24204     // private
24205     handleFailure : function(trans){
24206         this.trans = false;
24207         this.destroyTrans(trans, false);
24208         this.fireEvent("loadexception", this, null, trans.arg);
24209         trans.callback.call(trans.scope||window, null, trans.arg, false);
24210     }
24211 });/*
24212  * Based on:
24213  * Ext JS Library 1.1.1
24214  * Copyright(c) 2006-2007, Ext JS, LLC.
24215  *
24216  * Originally Released Under LGPL - original licence link has changed is not relivant.
24217  *
24218  * Fork - LGPL
24219  * <script type="text/javascript">
24220  */
24221
24222 /**
24223  * @class Roo.data.JsonReader
24224  * @extends Roo.data.DataReader
24225  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24226  * based on mappings in a provided Roo.data.Record constructor.
24227  * 
24228  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24229  * in the reply previously. 
24230  * 
24231  * <p>
24232  * Example code:
24233  * <pre><code>
24234 var RecordDef = Roo.data.Record.create([
24235     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24236     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24237 ]);
24238 var myReader = new Roo.data.JsonReader({
24239     totalProperty: "results",    // The property which contains the total dataset size (optional)
24240     root: "rows",                // The property which contains an Array of row objects
24241     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24242 }, RecordDef);
24243 </code></pre>
24244  * <p>
24245  * This would consume a JSON file like this:
24246  * <pre><code>
24247 { 'results': 2, 'rows': [
24248     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24249     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24250 }
24251 </code></pre>
24252  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24253  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24254  * paged from the remote server.
24255  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24256  * @cfg {String} root name of the property which contains the Array of row objects.
24257  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24258  * @cfg {Array} fields Array of field definition objects
24259  * @constructor
24260  * Create a new JsonReader
24261  * @param {Object} meta Metadata configuration options
24262  * @param {Object} recordType Either an Array of field definition objects,
24263  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24264  */
24265 Roo.data.JsonReader = function(meta, recordType){
24266     
24267     meta = meta || {};
24268     // set some defaults:
24269     Roo.applyIf(meta, {
24270         totalProperty: 'total',
24271         successProperty : 'success',
24272         root : 'data',
24273         id : 'id'
24274     });
24275     
24276     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24277 };
24278 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24279     
24280     /**
24281      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24282      * Used by Store query builder to append _requestMeta to params.
24283      * 
24284      */
24285     metaFromRemote : false,
24286     /**
24287      * This method is only used by a DataProxy which has retrieved data from a remote server.
24288      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24289      * @return {Object} data A data block which is used by an Roo.data.Store object as
24290      * a cache of Roo.data.Records.
24291      */
24292     read : function(response){
24293         var json = response.responseText;
24294        
24295         var o = /* eval:var:o */ eval("("+json+")");
24296         if(!o) {
24297             throw {message: "JsonReader.read: Json object not found"};
24298         }
24299         
24300         if(o.metaData){
24301             
24302             delete this.ef;
24303             this.metaFromRemote = true;
24304             this.meta = o.metaData;
24305             this.recordType = Roo.data.Record.create(o.metaData.fields);
24306             this.onMetaChange(this.meta, this.recordType, o);
24307         }
24308         return this.readRecords(o);
24309     },
24310
24311     // private function a store will implement
24312     onMetaChange : function(meta, recordType, o){
24313
24314     },
24315
24316     /**
24317          * @ignore
24318          */
24319     simpleAccess: function(obj, subsc) {
24320         return obj[subsc];
24321     },
24322
24323         /**
24324          * @ignore
24325          */
24326     getJsonAccessor: function(){
24327         var re = /[\[\.]/;
24328         return function(expr) {
24329             try {
24330                 return(re.test(expr))
24331                     ? new Function("obj", "return obj." + expr)
24332                     : function(obj){
24333                         return obj[expr];
24334                     };
24335             } catch(e){}
24336             return Roo.emptyFn;
24337         };
24338     }(),
24339
24340     /**
24341      * Create a data block containing Roo.data.Records from an XML document.
24342      * @param {Object} o An object which contains an Array of row objects in the property specified
24343      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24344      * which contains the total size of the dataset.
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     readRecords : function(o){
24349         /**
24350          * After any data loads, the raw JSON data is available for further custom processing.
24351          * @type Object
24352          */
24353         this.o = o;
24354         var s = this.meta, Record = this.recordType,
24355             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24356
24357 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24358         if (!this.ef) {
24359             if(s.totalProperty) {
24360                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24361                 }
24362                 if(s.successProperty) {
24363                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24364                 }
24365                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24366                 if (s.id) {
24367                         var g = this.getJsonAccessor(s.id);
24368                         this.getId = function(rec) {
24369                                 var r = g(rec);  
24370                                 return (r === undefined || r === "") ? null : r;
24371                         };
24372                 } else {
24373                         this.getId = function(){return null;};
24374                 }
24375             this.ef = [];
24376             for(var jj = 0; jj < fl; jj++){
24377                 f = fi[jj];
24378                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24379                 this.ef[jj] = this.getJsonAccessor(map);
24380             }
24381         }
24382
24383         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24384         if(s.totalProperty){
24385             var vt = parseInt(this.getTotal(o), 10);
24386             if(!isNaN(vt)){
24387                 totalRecords = vt;
24388             }
24389         }
24390         if(s.successProperty){
24391             var vs = this.getSuccess(o);
24392             if(vs === false || vs === 'false'){
24393                 success = false;
24394             }
24395         }
24396         var records = [];
24397         for(var i = 0; i < c; i++){
24398                 var n = root[i];
24399             var values = {};
24400             var id = this.getId(n);
24401             for(var j = 0; j < fl; j++){
24402                 f = fi[j];
24403             var v = this.ef[j](n);
24404             if (!f.convert) {
24405                 Roo.log('missing convert for ' + f.name);
24406                 Roo.log(f);
24407                 continue;
24408             }
24409             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24410             }
24411             var record = new Record(values, id);
24412             record.json = n;
24413             records[i] = record;
24414         }
24415         return {
24416             raw : o,
24417             success : success,
24418             records : records,
24419             totalRecords : totalRecords
24420         };
24421     }
24422 });/*
24423  * Based on:
24424  * Ext JS Library 1.1.1
24425  * Copyright(c) 2006-2007, Ext JS, LLC.
24426  *
24427  * Originally Released Under LGPL - original licence link has changed is not relivant.
24428  *
24429  * Fork - LGPL
24430  * <script type="text/javascript">
24431  */
24432
24433 /**
24434  * @class Roo.data.XmlReader
24435  * @extends Roo.data.DataReader
24436  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24437  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24438  * <p>
24439  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24440  * header in the HTTP response must be set to "text/xml".</em>
24441  * <p>
24442  * Example code:
24443  * <pre><code>
24444 var RecordDef = Roo.data.Record.create([
24445    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24446    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24447 ]);
24448 var myReader = new Roo.data.XmlReader({
24449    totalRecords: "results", // The element which contains the total dataset size (optional)
24450    record: "row",           // The repeated element which contains row information
24451    id: "id"                 // The element within the row that provides an ID for the record (optional)
24452 }, RecordDef);
24453 </code></pre>
24454  * <p>
24455  * This would consume an XML file like this:
24456  * <pre><code>
24457 &lt;?xml?>
24458 &lt;dataset>
24459  &lt;results>2&lt;/results>
24460  &lt;row>
24461    &lt;id>1&lt;/id>
24462    &lt;name>Bill&lt;/name>
24463    &lt;occupation>Gardener&lt;/occupation>
24464  &lt;/row>
24465  &lt;row>
24466    &lt;id>2&lt;/id>
24467    &lt;name>Ben&lt;/name>
24468    &lt;occupation>Horticulturalist&lt;/occupation>
24469  &lt;/row>
24470 &lt;/dataset>
24471 </code></pre>
24472  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24473  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24474  * paged from the remote server.
24475  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24476  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24477  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24478  * a record identifier value.
24479  * @constructor
24480  * Create a new XmlReader
24481  * @param {Object} meta Metadata configuration options
24482  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24483  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24484  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24485  */
24486 Roo.data.XmlReader = function(meta, recordType){
24487     meta = meta || {};
24488     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24489 };
24490 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24491     /**
24492      * This method is only used by a DataProxy which has retrieved data from a remote server.
24493          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24494          * to contain a method called 'responseXML' that returns an XML document object.
24495      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24496      * a cache of Roo.data.Records.
24497      */
24498     read : function(response){
24499         var doc = response.responseXML;
24500         if(!doc) {
24501             throw {message: "XmlReader.read: XML Document not available"};
24502         }
24503         return this.readRecords(doc);
24504     },
24505
24506     /**
24507      * Create a data block containing Roo.data.Records from an XML document.
24508          * @param {Object} doc A parsed XML document.
24509      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24510      * a cache of Roo.data.Records.
24511      */
24512     readRecords : function(doc){
24513         /**
24514          * After any data loads/reads, the raw XML Document is available for further custom processing.
24515          * @type XMLDocument
24516          */
24517         this.xmlData = doc;
24518         var root = doc.documentElement || doc;
24519         var q = Roo.DomQuery;
24520         var recordType = this.recordType, fields = recordType.prototype.fields;
24521         var sid = this.meta.id;
24522         var totalRecords = 0, success = true;
24523         if(this.meta.totalRecords){
24524             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24525         }
24526         
24527         if(this.meta.success){
24528             var sv = q.selectValue(this.meta.success, root, true);
24529             success = sv !== false && sv !== 'false';
24530         }
24531         var records = [];
24532         var ns = q.select(this.meta.record, root);
24533         for(var i = 0, len = ns.length; i < len; i++) {
24534                 var n = ns[i];
24535                 var values = {};
24536                 var id = sid ? q.selectValue(sid, n) : undefined;
24537                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24538                     var f = fields.items[j];
24539                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24540                     v = f.convert(v);
24541                     values[f.name] = v;
24542                 }
24543                 var record = new recordType(values, id);
24544                 record.node = n;
24545                 records[records.length] = record;
24546             }
24547
24548             return {
24549                 success : success,
24550                 records : records,
24551                 totalRecords : totalRecords || records.length
24552             };
24553     }
24554 });/*
24555  * Based on:
24556  * Ext JS Library 1.1.1
24557  * Copyright(c) 2006-2007, Ext JS, LLC.
24558  *
24559  * Originally Released Under LGPL - original licence link has changed is not relivant.
24560  *
24561  * Fork - LGPL
24562  * <script type="text/javascript">
24563  */
24564
24565 /**
24566  * @class Roo.data.ArrayReader
24567  * @extends Roo.data.DataReader
24568  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24569  * Each element of that Array represents a row of data fields. The
24570  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24571  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24572  * <p>
24573  * Example code:.
24574  * <pre><code>
24575 var RecordDef = Roo.data.Record.create([
24576     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24577     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24578 ]);
24579 var myReader = new Roo.data.ArrayReader({
24580     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24581 }, RecordDef);
24582 </code></pre>
24583  * <p>
24584  * This would consume an Array like this:
24585  * <pre><code>
24586 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24587   </code></pre>
24588  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24589  * @constructor
24590  * Create a new JsonReader
24591  * @param {Object} meta Metadata configuration options.
24592  * @param {Object} recordType Either an Array of field definition objects
24593  * as specified to {@link Roo.data.Record#create},
24594  * or an {@link Roo.data.Record} object
24595  * created using {@link Roo.data.Record#create}.
24596  */
24597 Roo.data.ArrayReader = function(meta, recordType){
24598     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24599 };
24600
24601 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24602     /**
24603      * Create a data block containing Roo.data.Records from an XML document.
24604      * @param {Object} o An Array of row objects which represents the dataset.
24605      * @return {Object} data A data block which is used by an Roo.data.Store object as
24606      * a cache of Roo.data.Records.
24607      */
24608     readRecords : function(o){
24609         var sid = this.meta ? this.meta.id : null;
24610         var recordType = this.recordType, fields = recordType.prototype.fields;
24611         var records = [];
24612         var root = o;
24613             for(var i = 0; i < root.length; i++){
24614                     var n = root[i];
24615                 var values = {};
24616                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24617                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24618                 var f = fields.items[j];
24619                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24620                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24621                 v = f.convert(v);
24622                 values[f.name] = v;
24623             }
24624                 var record = new recordType(values, id);
24625                 record.json = n;
24626                 records[records.length] = record;
24627             }
24628             return {
24629                 records : records,
24630                 totalRecords : records.length
24631             };
24632     }
24633 });/*
24634  * Based on:
24635  * Ext JS Library 1.1.1
24636  * Copyright(c) 2006-2007, Ext JS, LLC.
24637  *
24638  * Originally Released Under LGPL - original licence link has changed is not relivant.
24639  *
24640  * Fork - LGPL
24641  * <script type="text/javascript">
24642  */
24643
24644
24645 /**
24646  * @class Roo.data.Tree
24647  * @extends Roo.util.Observable
24648  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24649  * in the tree have most standard DOM functionality.
24650  * @constructor
24651  * @param {Node} root (optional) The root node
24652  */
24653 Roo.data.Tree = function(root){
24654    this.nodeHash = {};
24655    /**
24656     * The root node for this tree
24657     * @type Node
24658     */
24659    this.root = null;
24660    if(root){
24661        this.setRootNode(root);
24662    }
24663    this.addEvents({
24664        /**
24665         * @event append
24666         * Fires when a new child node is appended to a node in this tree.
24667         * @param {Tree} tree The owner tree
24668         * @param {Node} parent The parent node
24669         * @param {Node} node The newly appended node
24670         * @param {Number} index The index of the newly appended node
24671         */
24672        "append" : true,
24673        /**
24674         * @event remove
24675         * Fires when a child node is removed from a node in this tree.
24676         * @param {Tree} tree The owner tree
24677         * @param {Node} parent The parent node
24678         * @param {Node} node The child node removed
24679         */
24680        "remove" : true,
24681        /**
24682         * @event move
24683         * Fires when a node is moved to a new location in the tree
24684         * @param {Tree} tree The owner tree
24685         * @param {Node} node The node moved
24686         * @param {Node} oldParent The old parent of this node
24687         * @param {Node} newParent The new parent of this node
24688         * @param {Number} index The index it was moved to
24689         */
24690        "move" : true,
24691        /**
24692         * @event insert
24693         * Fires when a new child node is inserted in a node in this tree.
24694         * @param {Tree} tree The owner tree
24695         * @param {Node} parent The parent node
24696         * @param {Node} node The child node inserted
24697         * @param {Node} refNode The child node the node was inserted before
24698         */
24699        "insert" : true,
24700        /**
24701         * @event beforeappend
24702         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24703         * @param {Tree} tree The owner tree
24704         * @param {Node} parent The parent node
24705         * @param {Node} node The child node to be appended
24706         */
24707        "beforeappend" : true,
24708        /**
24709         * @event beforeremove
24710         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24711         * @param {Tree} tree The owner tree
24712         * @param {Node} parent The parent node
24713         * @param {Node} node The child node to be removed
24714         */
24715        "beforeremove" : true,
24716        /**
24717         * @event beforemove
24718         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24719         * @param {Tree} tree The owner tree
24720         * @param {Node} node The node being moved
24721         * @param {Node} oldParent The parent of the node
24722         * @param {Node} newParent The new parent the node is moving to
24723         * @param {Number} index The index it is being moved to
24724         */
24725        "beforemove" : true,
24726        /**
24727         * @event beforeinsert
24728         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24729         * @param {Tree} tree The owner tree
24730         * @param {Node} parent The parent node
24731         * @param {Node} node The child node to be inserted
24732         * @param {Node} refNode The child node the node is being inserted before
24733         */
24734        "beforeinsert" : true
24735    });
24736
24737     Roo.data.Tree.superclass.constructor.call(this);
24738 };
24739
24740 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24741     pathSeparator: "/",
24742
24743     proxyNodeEvent : function(){
24744         return this.fireEvent.apply(this, arguments);
24745     },
24746
24747     /**
24748      * Returns the root node for this tree.
24749      * @return {Node}
24750      */
24751     getRootNode : function(){
24752         return this.root;
24753     },
24754
24755     /**
24756      * Sets the root node for this tree.
24757      * @param {Node} node
24758      * @return {Node}
24759      */
24760     setRootNode : function(node){
24761         this.root = node;
24762         node.ownerTree = this;
24763         node.isRoot = true;
24764         this.registerNode(node);
24765         return node;
24766     },
24767
24768     /**
24769      * Gets a node in this tree by its id.
24770      * @param {String} id
24771      * @return {Node}
24772      */
24773     getNodeById : function(id){
24774         return this.nodeHash[id];
24775     },
24776
24777     registerNode : function(node){
24778         this.nodeHash[node.id] = node;
24779     },
24780
24781     unregisterNode : function(node){
24782         delete this.nodeHash[node.id];
24783     },
24784
24785     toString : function(){
24786         return "[Tree"+(this.id?" "+this.id:"")+"]";
24787     }
24788 });
24789
24790 /**
24791  * @class Roo.data.Node
24792  * @extends Roo.util.Observable
24793  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24794  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24795  * @constructor
24796  * @param {Object} attributes The attributes/config for the node
24797  */
24798 Roo.data.Node = function(attributes){
24799     /**
24800      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24801      * @type {Object}
24802      */
24803     this.attributes = attributes || {};
24804     this.leaf = this.attributes.leaf;
24805     /**
24806      * The node id. @type String
24807      */
24808     this.id = this.attributes.id;
24809     if(!this.id){
24810         this.id = Roo.id(null, "ynode-");
24811         this.attributes.id = this.id;
24812     }
24813      
24814     
24815     /**
24816      * All child nodes of this node. @type Array
24817      */
24818     this.childNodes = [];
24819     if(!this.childNodes.indexOf){ // indexOf is a must
24820         this.childNodes.indexOf = function(o){
24821             for(var i = 0, len = this.length; i < len; i++){
24822                 if(this[i] == o) {
24823                     return i;
24824                 }
24825             }
24826             return -1;
24827         };
24828     }
24829     /**
24830      * The parent node for this node. @type Node
24831      */
24832     this.parentNode = null;
24833     /**
24834      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24835      */
24836     this.firstChild = null;
24837     /**
24838      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24839      */
24840     this.lastChild = null;
24841     /**
24842      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24843      */
24844     this.previousSibling = null;
24845     /**
24846      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24847      */
24848     this.nextSibling = null;
24849
24850     this.addEvents({
24851        /**
24852         * @event append
24853         * Fires when a new child node is appended
24854         * @param {Tree} tree The owner tree
24855         * @param {Node} this This node
24856         * @param {Node} node The newly appended node
24857         * @param {Number} index The index of the newly appended node
24858         */
24859        "append" : true,
24860        /**
24861         * @event remove
24862         * Fires when a child node is removed
24863         * @param {Tree} tree The owner tree
24864         * @param {Node} this This node
24865         * @param {Node} node The removed node
24866         */
24867        "remove" : true,
24868        /**
24869         * @event move
24870         * Fires when this node is moved to a new location in the tree
24871         * @param {Tree} tree The owner tree
24872         * @param {Node} this This node
24873         * @param {Node} oldParent The old parent of this node
24874         * @param {Node} newParent The new parent of this node
24875         * @param {Number} index The index it was moved to
24876         */
24877        "move" : true,
24878        /**
24879         * @event insert
24880         * Fires when a new child node is inserted.
24881         * @param {Tree} tree The owner tree
24882         * @param {Node} this This node
24883         * @param {Node} node The child node inserted
24884         * @param {Node} refNode The child node the node was inserted before
24885         */
24886        "insert" : true,
24887        /**
24888         * @event beforeappend
24889         * Fires before a new child is appended, return false to cancel the append.
24890         * @param {Tree} tree The owner tree
24891         * @param {Node} this This node
24892         * @param {Node} node The child node to be appended
24893         */
24894        "beforeappend" : true,
24895        /**
24896         * @event beforeremove
24897         * Fires before a child is removed, return false to cancel the remove.
24898         * @param {Tree} tree The owner tree
24899         * @param {Node} this This node
24900         * @param {Node} node The child node to be removed
24901         */
24902        "beforeremove" : true,
24903        /**
24904         * @event beforemove
24905         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24906         * @param {Tree} tree The owner tree
24907         * @param {Node} this This node
24908         * @param {Node} oldParent The parent of this node
24909         * @param {Node} newParent The new parent this node is moving to
24910         * @param {Number} index The index it is being moved to
24911         */
24912        "beforemove" : true,
24913        /**
24914         * @event beforeinsert
24915         * Fires before a new child is inserted, return false to cancel the insert.
24916         * @param {Tree} tree The owner tree
24917         * @param {Node} this This node
24918         * @param {Node} node The child node to be inserted
24919         * @param {Node} refNode The child node the node is being inserted before
24920         */
24921        "beforeinsert" : true
24922    });
24923     this.listeners = this.attributes.listeners;
24924     Roo.data.Node.superclass.constructor.call(this);
24925 };
24926
24927 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24928     fireEvent : function(evtName){
24929         // first do standard event for this node
24930         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24931             return false;
24932         }
24933         // then bubble it up to the tree if the event wasn't cancelled
24934         var ot = this.getOwnerTree();
24935         if(ot){
24936             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24937                 return false;
24938             }
24939         }
24940         return true;
24941     },
24942
24943     /**
24944      * Returns true if this node is a leaf
24945      * @return {Boolean}
24946      */
24947     isLeaf : function(){
24948         return this.leaf === true;
24949     },
24950
24951     // private
24952     setFirstChild : function(node){
24953         this.firstChild = node;
24954     },
24955
24956     //private
24957     setLastChild : function(node){
24958         this.lastChild = node;
24959     },
24960
24961
24962     /**
24963      * Returns true if this node is the last child of its parent
24964      * @return {Boolean}
24965      */
24966     isLast : function(){
24967        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24968     },
24969
24970     /**
24971      * Returns true if this node is the first child of its parent
24972      * @return {Boolean}
24973      */
24974     isFirst : function(){
24975        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24976     },
24977
24978     hasChildNodes : function(){
24979         return !this.isLeaf() && this.childNodes.length > 0;
24980     },
24981
24982     /**
24983      * Insert node(s) as the last child node of this node.
24984      * @param {Node/Array} node The node or Array of nodes to append
24985      * @return {Node} The appended node if single append, or null if an array was passed
24986      */
24987     appendChild : function(node){
24988         var multi = false;
24989         if(node instanceof Array){
24990             multi = node;
24991         }else if(arguments.length > 1){
24992             multi = arguments;
24993         }
24994         // if passed an array or multiple args do them one by one
24995         if(multi){
24996             for(var i = 0, len = multi.length; i < len; i++) {
24997                 this.appendChild(multi[i]);
24998             }
24999         }else{
25000             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25001                 return false;
25002             }
25003             var index = this.childNodes.length;
25004             var oldParent = node.parentNode;
25005             // it's a move, make sure we move it cleanly
25006             if(oldParent){
25007                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25008                     return false;
25009                 }
25010                 oldParent.removeChild(node);
25011             }
25012             index = this.childNodes.length;
25013             if(index == 0){
25014                 this.setFirstChild(node);
25015             }
25016             this.childNodes.push(node);
25017             node.parentNode = this;
25018             var ps = this.childNodes[index-1];
25019             if(ps){
25020                 node.previousSibling = ps;
25021                 ps.nextSibling = node;
25022             }else{
25023                 node.previousSibling = null;
25024             }
25025             node.nextSibling = null;
25026             this.setLastChild(node);
25027             node.setOwnerTree(this.getOwnerTree());
25028             this.fireEvent("append", this.ownerTree, this, node, index);
25029             if(oldParent){
25030                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25031             }
25032             return node;
25033         }
25034     },
25035
25036     /**
25037      * Removes a child node from this node.
25038      * @param {Node} node The node to remove
25039      * @return {Node} The removed node
25040      */
25041     removeChild : function(node){
25042         var index = this.childNodes.indexOf(node);
25043         if(index == -1){
25044             return false;
25045         }
25046         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25047             return false;
25048         }
25049
25050         // remove it from childNodes collection
25051         this.childNodes.splice(index, 1);
25052
25053         // update siblings
25054         if(node.previousSibling){
25055             node.previousSibling.nextSibling = node.nextSibling;
25056         }
25057         if(node.nextSibling){
25058             node.nextSibling.previousSibling = node.previousSibling;
25059         }
25060
25061         // update child refs
25062         if(this.firstChild == node){
25063             this.setFirstChild(node.nextSibling);
25064         }
25065         if(this.lastChild == node){
25066             this.setLastChild(node.previousSibling);
25067         }
25068
25069         node.setOwnerTree(null);
25070         // clear any references from the node
25071         node.parentNode = null;
25072         node.previousSibling = null;
25073         node.nextSibling = null;
25074         this.fireEvent("remove", this.ownerTree, this, node);
25075         return node;
25076     },
25077
25078     /**
25079      * Inserts the first node before the second node in this nodes childNodes collection.
25080      * @param {Node} node The node to insert
25081      * @param {Node} refNode The node to insert before (if null the node is appended)
25082      * @return {Node} The inserted node
25083      */
25084     insertBefore : function(node, refNode){
25085         if(!refNode){ // like standard Dom, refNode can be null for append
25086             return this.appendChild(node);
25087         }
25088         // nothing to do
25089         if(node == refNode){
25090             return false;
25091         }
25092
25093         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25094             return false;
25095         }
25096         var index = this.childNodes.indexOf(refNode);
25097         var oldParent = node.parentNode;
25098         var refIndex = index;
25099
25100         // when moving internally, indexes will change after remove
25101         if(oldParent == this && this.childNodes.indexOf(node) < index){
25102             refIndex--;
25103         }
25104
25105         // it's a move, make sure we move it cleanly
25106         if(oldParent){
25107             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25108                 return false;
25109             }
25110             oldParent.removeChild(node);
25111         }
25112         if(refIndex == 0){
25113             this.setFirstChild(node);
25114         }
25115         this.childNodes.splice(refIndex, 0, node);
25116         node.parentNode = this;
25117         var ps = this.childNodes[refIndex-1];
25118         if(ps){
25119             node.previousSibling = ps;
25120             ps.nextSibling = node;
25121         }else{
25122             node.previousSibling = null;
25123         }
25124         node.nextSibling = refNode;
25125         refNode.previousSibling = node;
25126         node.setOwnerTree(this.getOwnerTree());
25127         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25128         if(oldParent){
25129             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25130         }
25131         return node;
25132     },
25133
25134     /**
25135      * Returns the child node at the specified index.
25136      * @param {Number} index
25137      * @return {Node}
25138      */
25139     item : function(index){
25140         return this.childNodes[index];
25141     },
25142
25143     /**
25144      * Replaces one child node in this node with another.
25145      * @param {Node} newChild The replacement node
25146      * @param {Node} oldChild The node to replace
25147      * @return {Node} The replaced node
25148      */
25149     replaceChild : function(newChild, oldChild){
25150         this.insertBefore(newChild, oldChild);
25151         this.removeChild(oldChild);
25152         return oldChild;
25153     },
25154
25155     /**
25156      * Returns the index of a child node
25157      * @param {Node} node
25158      * @return {Number} The index of the node or -1 if it was not found
25159      */
25160     indexOf : function(child){
25161         return this.childNodes.indexOf(child);
25162     },
25163
25164     /**
25165      * Returns the tree this node is in.
25166      * @return {Tree}
25167      */
25168     getOwnerTree : function(){
25169         // if it doesn't have one, look for one
25170         if(!this.ownerTree){
25171             var p = this;
25172             while(p){
25173                 if(p.ownerTree){
25174                     this.ownerTree = p.ownerTree;
25175                     break;
25176                 }
25177                 p = p.parentNode;
25178             }
25179         }
25180         return this.ownerTree;
25181     },
25182
25183     /**
25184      * Returns depth of this node (the root node has a depth of 0)
25185      * @return {Number}
25186      */
25187     getDepth : function(){
25188         var depth = 0;
25189         var p = this;
25190         while(p.parentNode){
25191             ++depth;
25192             p = p.parentNode;
25193         }
25194         return depth;
25195     },
25196
25197     // private
25198     setOwnerTree : function(tree){
25199         // if it's move, we need to update everyone
25200         if(tree != this.ownerTree){
25201             if(this.ownerTree){
25202                 this.ownerTree.unregisterNode(this);
25203             }
25204             this.ownerTree = tree;
25205             var cs = this.childNodes;
25206             for(var i = 0, len = cs.length; i < len; i++) {
25207                 cs[i].setOwnerTree(tree);
25208             }
25209             if(tree){
25210                 tree.registerNode(this);
25211             }
25212         }
25213     },
25214
25215     /**
25216      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25217      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25218      * @return {String} The path
25219      */
25220     getPath : function(attr){
25221         attr = attr || "id";
25222         var p = this.parentNode;
25223         var b = [this.attributes[attr]];
25224         while(p){
25225             b.unshift(p.attributes[attr]);
25226             p = p.parentNode;
25227         }
25228         var sep = this.getOwnerTree().pathSeparator;
25229         return sep + b.join(sep);
25230     },
25231
25232     /**
25233      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25234      * function call will be the scope provided or the current node. The arguments to the function
25235      * will be the args provided or the current node. If the function returns false at any point,
25236      * the bubble is stopped.
25237      * @param {Function} fn The function to call
25238      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25239      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25240      */
25241     bubble : function(fn, scope, args){
25242         var p = this;
25243         while(p){
25244             if(fn.call(scope || p, args || p) === false){
25245                 break;
25246             }
25247             p = p.parentNode;
25248         }
25249     },
25250
25251     /**
25252      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25253      * function call will be the scope provided or the current node. The arguments to the function
25254      * will be the args provided or the current node. If the function returns false at any point,
25255      * the cascade is stopped on that branch.
25256      * @param {Function} fn The function to call
25257      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25258      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25259      */
25260     cascade : function(fn, scope, args){
25261         if(fn.call(scope || this, args || this) !== false){
25262             var cs = this.childNodes;
25263             for(var i = 0, len = cs.length; i < len; i++) {
25264                 cs[i].cascade(fn, scope, args);
25265             }
25266         }
25267     },
25268
25269     /**
25270      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25271      * function call will be the scope provided or the current node. The arguments to the function
25272      * will be the args provided or the current node. If the function returns false at any point,
25273      * the iteration stops.
25274      * @param {Function} fn The function to call
25275      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25276      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25277      */
25278     eachChild : function(fn, scope, args){
25279         var cs = this.childNodes;
25280         for(var i = 0, len = cs.length; i < len; i++) {
25281                 if(fn.call(scope || this, args || cs[i]) === false){
25282                     break;
25283                 }
25284         }
25285     },
25286
25287     /**
25288      * Finds the first child that has the attribute with the specified value.
25289      * @param {String} attribute The attribute name
25290      * @param {Mixed} value The value to search for
25291      * @return {Node} The found child or null if none was found
25292      */
25293     findChild : function(attribute, value){
25294         var cs = this.childNodes;
25295         for(var i = 0, len = cs.length; i < len; i++) {
25296                 if(cs[i].attributes[attribute] == value){
25297                     return cs[i];
25298                 }
25299         }
25300         return null;
25301     },
25302
25303     /**
25304      * Finds the first child by a custom function. The child matches if the function passed
25305      * returns true.
25306      * @param {Function} fn
25307      * @param {Object} scope (optional)
25308      * @return {Node} The found child or null if none was found
25309      */
25310     findChildBy : function(fn, scope){
25311         var cs = this.childNodes;
25312         for(var i = 0, len = cs.length; i < len; i++) {
25313                 if(fn.call(scope||cs[i], cs[i]) === true){
25314                     return cs[i];
25315                 }
25316         }
25317         return null;
25318     },
25319
25320     /**
25321      * Sorts this nodes children using the supplied sort function
25322      * @param {Function} fn
25323      * @param {Object} scope (optional)
25324      */
25325     sort : function(fn, scope){
25326         var cs = this.childNodes;
25327         var len = cs.length;
25328         if(len > 0){
25329             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25330             cs.sort(sortFn);
25331             for(var i = 0; i < len; i++){
25332                 var n = cs[i];
25333                 n.previousSibling = cs[i-1];
25334                 n.nextSibling = cs[i+1];
25335                 if(i == 0){
25336                     this.setFirstChild(n);
25337                 }
25338                 if(i == len-1){
25339                     this.setLastChild(n);
25340                 }
25341             }
25342         }
25343     },
25344
25345     /**
25346      * Returns true if this node is an ancestor (at any point) of the passed node.
25347      * @param {Node} node
25348      * @return {Boolean}
25349      */
25350     contains : function(node){
25351         return node.isAncestor(this);
25352     },
25353
25354     /**
25355      * Returns true if the passed node is an ancestor (at any point) of this node.
25356      * @param {Node} node
25357      * @return {Boolean}
25358      */
25359     isAncestor : function(node){
25360         var p = this.parentNode;
25361         while(p){
25362             if(p == node){
25363                 return true;
25364             }
25365             p = p.parentNode;
25366         }
25367         return false;
25368     },
25369
25370     toString : function(){
25371         return "[Node"+(this.id?" "+this.id:"")+"]";
25372     }
25373 });/*
25374  * Based on:
25375  * Ext JS Library 1.1.1
25376  * Copyright(c) 2006-2007, Ext JS, LLC.
25377  *
25378  * Originally Released Under LGPL - original licence link has changed is not relivant.
25379  *
25380  * Fork - LGPL
25381  * <script type="text/javascript">
25382  */
25383  (function(){ 
25384 /**
25385  * @class Roo.Layer
25386  * @extends Roo.Element
25387  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25388  * automatic maintaining of shadow/shim positions.
25389  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25390  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25391  * you can pass a string with a CSS class name. False turns off the shadow.
25392  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25393  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25394  * @cfg {String} cls CSS class to add to the element
25395  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25396  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25397  * @constructor
25398  * @param {Object} config An object with config options.
25399  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25400  */
25401
25402 Roo.Layer = function(config, existingEl){
25403     config = config || {};
25404     var dh = Roo.DomHelper;
25405     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25406     if(existingEl){
25407         this.dom = Roo.getDom(existingEl);
25408     }
25409     if(!this.dom){
25410         var o = config.dh || {tag: "div", cls: "x-layer"};
25411         this.dom = dh.append(pel, o);
25412     }
25413     if(config.cls){
25414         this.addClass(config.cls);
25415     }
25416     this.constrain = config.constrain !== false;
25417     this.visibilityMode = Roo.Element.VISIBILITY;
25418     if(config.id){
25419         this.id = this.dom.id = config.id;
25420     }else{
25421         this.id = Roo.id(this.dom);
25422     }
25423     this.zindex = config.zindex || this.getZIndex();
25424     this.position("absolute", this.zindex);
25425     if(config.shadow){
25426         this.shadowOffset = config.shadowOffset || 4;
25427         this.shadow = new Roo.Shadow({
25428             offset : this.shadowOffset,
25429             mode : config.shadow
25430         });
25431     }else{
25432         this.shadowOffset = 0;
25433     }
25434     this.useShim = config.shim !== false && Roo.useShims;
25435     this.useDisplay = config.useDisplay;
25436     this.hide();
25437 };
25438
25439 var supr = Roo.Element.prototype;
25440
25441 // shims are shared among layer to keep from having 100 iframes
25442 var shims = [];
25443
25444 Roo.extend(Roo.Layer, Roo.Element, {
25445
25446     getZIndex : function(){
25447         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25448     },
25449
25450     getShim : function(){
25451         if(!this.useShim){
25452             return null;
25453         }
25454         if(this.shim){
25455             return this.shim;
25456         }
25457         var shim = shims.shift();
25458         if(!shim){
25459             shim = this.createShim();
25460             shim.enableDisplayMode('block');
25461             shim.dom.style.display = 'none';
25462             shim.dom.style.visibility = 'visible';
25463         }
25464         var pn = this.dom.parentNode;
25465         if(shim.dom.parentNode != pn){
25466             pn.insertBefore(shim.dom, this.dom);
25467         }
25468         shim.setStyle('z-index', this.getZIndex()-2);
25469         this.shim = shim;
25470         return shim;
25471     },
25472
25473     hideShim : function(){
25474         if(this.shim){
25475             this.shim.setDisplayed(false);
25476             shims.push(this.shim);
25477             delete this.shim;
25478         }
25479     },
25480
25481     disableShadow : function(){
25482         if(this.shadow){
25483             this.shadowDisabled = true;
25484             this.shadow.hide();
25485             this.lastShadowOffset = this.shadowOffset;
25486             this.shadowOffset = 0;
25487         }
25488     },
25489
25490     enableShadow : function(show){
25491         if(this.shadow){
25492             this.shadowDisabled = false;
25493             this.shadowOffset = this.lastShadowOffset;
25494             delete this.lastShadowOffset;
25495             if(show){
25496                 this.sync(true);
25497             }
25498         }
25499     },
25500
25501     // private
25502     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25503     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25504     sync : function(doShow){
25505         var sw = this.shadow;
25506         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25507             var sh = this.getShim();
25508
25509             var w = this.getWidth(),
25510                 h = this.getHeight();
25511
25512             var l = this.getLeft(true),
25513                 t = this.getTop(true);
25514
25515             if(sw && !this.shadowDisabled){
25516                 if(doShow && !sw.isVisible()){
25517                     sw.show(this);
25518                 }else{
25519                     sw.realign(l, t, w, h);
25520                 }
25521                 if(sh){
25522                     if(doShow){
25523                        sh.show();
25524                     }
25525                     // fit the shim behind the shadow, so it is shimmed too
25526                     var a = sw.adjusts, s = sh.dom.style;
25527                     s.left = (Math.min(l, l+a.l))+"px";
25528                     s.top = (Math.min(t, t+a.t))+"px";
25529                     s.width = (w+a.w)+"px";
25530                     s.height = (h+a.h)+"px";
25531                 }
25532             }else if(sh){
25533                 if(doShow){
25534                    sh.show();
25535                 }
25536                 sh.setSize(w, h);
25537                 sh.setLeftTop(l, t);
25538             }
25539             
25540         }
25541     },
25542
25543     // private
25544     destroy : function(){
25545         this.hideShim();
25546         if(this.shadow){
25547             this.shadow.hide();
25548         }
25549         this.removeAllListeners();
25550         var pn = this.dom.parentNode;
25551         if(pn){
25552             pn.removeChild(this.dom);
25553         }
25554         Roo.Element.uncache(this.id);
25555     },
25556
25557     remove : function(){
25558         this.destroy();
25559     },
25560
25561     // private
25562     beginUpdate : function(){
25563         this.updating = true;
25564     },
25565
25566     // private
25567     endUpdate : function(){
25568         this.updating = false;
25569         this.sync(true);
25570     },
25571
25572     // private
25573     hideUnders : function(negOffset){
25574         if(this.shadow){
25575             this.shadow.hide();
25576         }
25577         this.hideShim();
25578     },
25579
25580     // private
25581     constrainXY : function(){
25582         if(this.constrain){
25583             var vw = Roo.lib.Dom.getViewWidth(),
25584                 vh = Roo.lib.Dom.getViewHeight();
25585             var s = Roo.get(document).getScroll();
25586
25587             var xy = this.getXY();
25588             var x = xy[0], y = xy[1];   
25589             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25590             // only move it if it needs it
25591             var moved = false;
25592             // first validate right/bottom
25593             if((x + w) > vw+s.left){
25594                 x = vw - w - this.shadowOffset;
25595                 moved = true;
25596             }
25597             if((y + h) > vh+s.top){
25598                 y = vh - h - this.shadowOffset;
25599                 moved = true;
25600             }
25601             // then make sure top/left isn't negative
25602             if(x < s.left){
25603                 x = s.left;
25604                 moved = true;
25605             }
25606             if(y < s.top){
25607                 y = s.top;
25608                 moved = true;
25609             }
25610             if(moved){
25611                 if(this.avoidY){
25612                     var ay = this.avoidY;
25613                     if(y <= ay && (y+h) >= ay){
25614                         y = ay-h-5;   
25615                     }
25616                 }
25617                 xy = [x, y];
25618                 this.storeXY(xy);
25619                 supr.setXY.call(this, xy);
25620                 this.sync();
25621             }
25622         }
25623     },
25624
25625     isVisible : function(){
25626         return this.visible;    
25627     },
25628
25629     // private
25630     showAction : function(){
25631         this.visible = true; // track visibility to prevent getStyle calls
25632         if(this.useDisplay === true){
25633             this.setDisplayed("");
25634         }else if(this.lastXY){
25635             supr.setXY.call(this, this.lastXY);
25636         }else if(this.lastLT){
25637             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25638         }
25639     },
25640
25641     // private
25642     hideAction : function(){
25643         this.visible = false;
25644         if(this.useDisplay === true){
25645             this.setDisplayed(false);
25646         }else{
25647             this.setLeftTop(-10000,-10000);
25648         }
25649     },
25650
25651     // overridden Element method
25652     setVisible : function(v, a, d, c, e){
25653         if(v){
25654             this.showAction();
25655         }
25656         if(a && v){
25657             var cb = function(){
25658                 this.sync(true);
25659                 if(c){
25660                     c();
25661                 }
25662             }.createDelegate(this);
25663             supr.setVisible.call(this, true, true, d, cb, e);
25664         }else{
25665             if(!v){
25666                 this.hideUnders(true);
25667             }
25668             var cb = c;
25669             if(a){
25670                 cb = function(){
25671                     this.hideAction();
25672                     if(c){
25673                         c();
25674                     }
25675                 }.createDelegate(this);
25676             }
25677             supr.setVisible.call(this, v, a, d, cb, e);
25678             if(v){
25679                 this.sync(true);
25680             }else if(!a){
25681                 this.hideAction();
25682             }
25683         }
25684     },
25685
25686     storeXY : function(xy){
25687         delete this.lastLT;
25688         this.lastXY = xy;
25689     },
25690
25691     storeLeftTop : function(left, top){
25692         delete this.lastXY;
25693         this.lastLT = [left, top];
25694     },
25695
25696     // private
25697     beforeFx : function(){
25698         this.beforeAction();
25699         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25700     },
25701
25702     // private
25703     afterFx : function(){
25704         Roo.Layer.superclass.afterFx.apply(this, arguments);
25705         this.sync(this.isVisible());
25706     },
25707
25708     // private
25709     beforeAction : function(){
25710         if(!this.updating && this.shadow){
25711             this.shadow.hide();
25712         }
25713     },
25714
25715     // overridden Element method
25716     setLeft : function(left){
25717         this.storeLeftTop(left, this.getTop(true));
25718         supr.setLeft.apply(this, arguments);
25719         this.sync();
25720     },
25721
25722     setTop : function(top){
25723         this.storeLeftTop(this.getLeft(true), top);
25724         supr.setTop.apply(this, arguments);
25725         this.sync();
25726     },
25727
25728     setLeftTop : function(left, top){
25729         this.storeLeftTop(left, top);
25730         supr.setLeftTop.apply(this, arguments);
25731         this.sync();
25732     },
25733
25734     setXY : function(xy, a, d, c, e){
25735         this.fixDisplay();
25736         this.beforeAction();
25737         this.storeXY(xy);
25738         var cb = this.createCB(c);
25739         supr.setXY.call(this, xy, a, d, cb, e);
25740         if(!a){
25741             cb();
25742         }
25743     },
25744
25745     // private
25746     createCB : function(c){
25747         var el = this;
25748         return function(){
25749             el.constrainXY();
25750             el.sync(true);
25751             if(c){
25752                 c();
25753             }
25754         };
25755     },
25756
25757     // overridden Element method
25758     setX : function(x, a, d, c, e){
25759         this.setXY([x, this.getY()], a, d, c, e);
25760     },
25761
25762     // overridden Element method
25763     setY : function(y, a, d, c, e){
25764         this.setXY([this.getX(), y], a, d, c, e);
25765     },
25766
25767     // overridden Element method
25768     setSize : function(w, h, a, d, c, e){
25769         this.beforeAction();
25770         var cb = this.createCB(c);
25771         supr.setSize.call(this, w, h, a, d, cb, e);
25772         if(!a){
25773             cb();
25774         }
25775     },
25776
25777     // overridden Element method
25778     setWidth : function(w, a, d, c, e){
25779         this.beforeAction();
25780         var cb = this.createCB(c);
25781         supr.setWidth.call(this, w, a, d, cb, e);
25782         if(!a){
25783             cb();
25784         }
25785     },
25786
25787     // overridden Element method
25788     setHeight : function(h, a, d, c, e){
25789         this.beforeAction();
25790         var cb = this.createCB(c);
25791         supr.setHeight.call(this, h, a, d, cb, e);
25792         if(!a){
25793             cb();
25794         }
25795     },
25796
25797     // overridden Element method
25798     setBounds : function(x, y, w, h, a, d, c, e){
25799         this.beforeAction();
25800         var cb = this.createCB(c);
25801         if(!a){
25802             this.storeXY([x, y]);
25803             supr.setXY.call(this, [x, y]);
25804             supr.setSize.call(this, w, h, a, d, cb, e);
25805             cb();
25806         }else{
25807             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25808         }
25809         return this;
25810     },
25811     
25812     /**
25813      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25814      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25815      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25816      * @param {Number} zindex The new z-index to set
25817      * @return {this} The Layer
25818      */
25819     setZIndex : function(zindex){
25820         this.zindex = zindex;
25821         this.setStyle("z-index", zindex + 2);
25822         if(this.shadow){
25823             this.shadow.setZIndex(zindex + 1);
25824         }
25825         if(this.shim){
25826             this.shim.setStyle("z-index", zindex);
25827         }
25828     }
25829 });
25830 })();/*
25831  * Based on:
25832  * Ext JS Library 1.1.1
25833  * Copyright(c) 2006-2007, Ext JS, LLC.
25834  *
25835  * Originally Released Under LGPL - original licence link has changed is not relivant.
25836  *
25837  * Fork - LGPL
25838  * <script type="text/javascript">
25839  */
25840
25841
25842 /**
25843  * @class Roo.Shadow
25844  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25845  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25846  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25847  * @constructor
25848  * Create a new Shadow
25849  * @param {Object} config The config object
25850  */
25851 Roo.Shadow = function(config){
25852     Roo.apply(this, config);
25853     if(typeof this.mode != "string"){
25854         this.mode = this.defaultMode;
25855     }
25856     var o = this.offset, a = {h: 0};
25857     var rad = Math.floor(this.offset/2);
25858     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25859         case "drop":
25860             a.w = 0;
25861             a.l = a.t = o;
25862             a.t -= 1;
25863             if(Roo.isIE){
25864                 a.l -= this.offset + rad;
25865                 a.t -= this.offset + rad;
25866                 a.w -= rad;
25867                 a.h -= rad;
25868                 a.t += 1;
25869             }
25870         break;
25871         case "sides":
25872             a.w = (o*2);
25873             a.l = -o;
25874             a.t = o-1;
25875             if(Roo.isIE){
25876                 a.l -= (this.offset - rad);
25877                 a.t -= this.offset + rad;
25878                 a.l += 1;
25879                 a.w -= (this.offset - rad)*2;
25880                 a.w -= rad + 1;
25881                 a.h -= 1;
25882             }
25883         break;
25884         case "frame":
25885             a.w = a.h = (o*2);
25886             a.l = a.t = -o;
25887             a.t += 1;
25888             a.h -= 2;
25889             if(Roo.isIE){
25890                 a.l -= (this.offset - rad);
25891                 a.t -= (this.offset - rad);
25892                 a.l += 1;
25893                 a.w -= (this.offset + rad + 1);
25894                 a.h -= (this.offset + rad);
25895                 a.h += 1;
25896             }
25897         break;
25898     };
25899
25900     this.adjusts = a;
25901 };
25902
25903 Roo.Shadow.prototype = {
25904     /**
25905      * @cfg {String} mode
25906      * The shadow display mode.  Supports the following options:<br />
25907      * sides: Shadow displays on both sides and bottom only<br />
25908      * frame: Shadow displays equally on all four sides<br />
25909      * drop: Traditional bottom-right drop shadow (default)
25910      */
25911     /**
25912      * @cfg {String} offset
25913      * The number of pixels to offset the shadow from the element (defaults to 4)
25914      */
25915     offset: 4,
25916
25917     // private
25918     defaultMode: "drop",
25919
25920     /**
25921      * Displays the shadow under the target element
25922      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25923      */
25924     show : function(target){
25925         target = Roo.get(target);
25926         if(!this.el){
25927             this.el = Roo.Shadow.Pool.pull();
25928             if(this.el.dom.nextSibling != target.dom){
25929                 this.el.insertBefore(target);
25930             }
25931         }
25932         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25933         if(Roo.isIE){
25934             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25935         }
25936         this.realign(
25937             target.getLeft(true),
25938             target.getTop(true),
25939             target.getWidth(),
25940             target.getHeight()
25941         );
25942         this.el.dom.style.display = "block";
25943     },
25944
25945     /**
25946      * Returns true if the shadow is visible, else false
25947      */
25948     isVisible : function(){
25949         return this.el ? true : false;  
25950     },
25951
25952     /**
25953      * Direct alignment when values are already available. Show must be called at least once before
25954      * calling this method to ensure it is initialized.
25955      * @param {Number} left The target element left position
25956      * @param {Number} top The target element top position
25957      * @param {Number} width The target element width
25958      * @param {Number} height The target element height
25959      */
25960     realign : function(l, t, w, h){
25961         if(!this.el){
25962             return;
25963         }
25964         var a = this.adjusts, d = this.el.dom, s = d.style;
25965         var iea = 0;
25966         s.left = (l+a.l)+"px";
25967         s.top = (t+a.t)+"px";
25968         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25969  
25970         if(s.width != sws || s.height != shs){
25971             s.width = sws;
25972             s.height = shs;
25973             if(!Roo.isIE){
25974                 var cn = d.childNodes;
25975                 var sww = Math.max(0, (sw-12))+"px";
25976                 cn[0].childNodes[1].style.width = sww;
25977                 cn[1].childNodes[1].style.width = sww;
25978                 cn[2].childNodes[1].style.width = sww;
25979                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25980             }
25981         }
25982     },
25983
25984     /**
25985      * Hides this shadow
25986      */
25987     hide : function(){
25988         if(this.el){
25989             this.el.dom.style.display = "none";
25990             Roo.Shadow.Pool.push(this.el);
25991             delete this.el;
25992         }
25993     },
25994
25995     /**
25996      * Adjust the z-index of this shadow
25997      * @param {Number} zindex The new z-index
25998      */
25999     setZIndex : function(z){
26000         this.zIndex = z;
26001         if(this.el){
26002             this.el.setStyle("z-index", z);
26003         }
26004     }
26005 };
26006
26007 // Private utility class that manages the internal Shadow cache
26008 Roo.Shadow.Pool = function(){
26009     var p = [];
26010     var markup = Roo.isIE ?
26011                  '<div class="x-ie-shadow"></div>' :
26012                  '<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>';
26013     return {
26014         pull : function(){
26015             var sh = p.shift();
26016             if(!sh){
26017                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26018                 sh.autoBoxAdjust = false;
26019             }
26020             return sh;
26021         },
26022
26023         push : function(sh){
26024             p.push(sh);
26025         }
26026     };
26027 }();/*
26028  * Based on:
26029  * Ext JS Library 1.1.1
26030  * Copyright(c) 2006-2007, Ext JS, LLC.
26031  *
26032  * Originally Released Under LGPL - original licence link has changed is not relivant.
26033  *
26034  * Fork - LGPL
26035  * <script type="text/javascript">
26036  */
26037
26038
26039 /**
26040  * @class Roo.SplitBar
26041  * @extends Roo.util.Observable
26042  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26043  * <br><br>
26044  * Usage:
26045  * <pre><code>
26046 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26047                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26048 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26049 split.minSize = 100;
26050 split.maxSize = 600;
26051 split.animate = true;
26052 split.on('moved', splitterMoved);
26053 </code></pre>
26054  * @constructor
26055  * Create a new SplitBar
26056  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26057  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26058  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26059  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26060                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26061                         position of the SplitBar).
26062  */
26063 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26064     
26065     /** @private */
26066     this.el = Roo.get(dragElement, true);
26067     this.el.dom.unselectable = "on";
26068     /** @private */
26069     this.resizingEl = Roo.get(resizingElement, true);
26070
26071     /**
26072      * @private
26073      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26074      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26075      * @type Number
26076      */
26077     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26078     
26079     /**
26080      * The minimum size of the resizing element. (Defaults to 0)
26081      * @type Number
26082      */
26083     this.minSize = 0;
26084     
26085     /**
26086      * The maximum size of the resizing element. (Defaults to 2000)
26087      * @type Number
26088      */
26089     this.maxSize = 2000;
26090     
26091     /**
26092      * Whether to animate the transition to the new size
26093      * @type Boolean
26094      */
26095     this.animate = false;
26096     
26097     /**
26098      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26099      * @type Boolean
26100      */
26101     this.useShim = false;
26102     
26103     /** @private */
26104     this.shim = null;
26105     
26106     if(!existingProxy){
26107         /** @private */
26108         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26109     }else{
26110         this.proxy = Roo.get(existingProxy).dom;
26111     }
26112     /** @private */
26113     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26114     
26115     /** @private */
26116     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26117     
26118     /** @private */
26119     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26120     
26121     /** @private */
26122     this.dragSpecs = {};
26123     
26124     /**
26125      * @private The adapter to use to positon and resize elements
26126      */
26127     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26128     this.adapter.init(this);
26129     
26130     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26131         /** @private */
26132         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26133         this.el.addClass("x-splitbar-h");
26134     }else{
26135         /** @private */
26136         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26137         this.el.addClass("x-splitbar-v");
26138     }
26139     
26140     this.addEvents({
26141         /**
26142          * @event resize
26143          * Fires when the splitter is moved (alias for {@link #event-moved})
26144          * @param {Roo.SplitBar} this
26145          * @param {Number} newSize the new width or height
26146          */
26147         "resize" : true,
26148         /**
26149          * @event moved
26150          * Fires when the splitter is moved
26151          * @param {Roo.SplitBar} this
26152          * @param {Number} newSize the new width or height
26153          */
26154         "moved" : true,
26155         /**
26156          * @event beforeresize
26157          * Fires before the splitter is dragged
26158          * @param {Roo.SplitBar} this
26159          */
26160         "beforeresize" : true,
26161
26162         "beforeapply" : true
26163     });
26164
26165     Roo.util.Observable.call(this);
26166 };
26167
26168 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26169     onStartProxyDrag : function(x, y){
26170         this.fireEvent("beforeresize", this);
26171         if(!this.overlay){
26172             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26173             o.unselectable();
26174             o.enableDisplayMode("block");
26175             // all splitbars share the same overlay
26176             Roo.SplitBar.prototype.overlay = o;
26177         }
26178         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26179         this.overlay.show();
26180         Roo.get(this.proxy).setDisplayed("block");
26181         var size = this.adapter.getElementSize(this);
26182         this.activeMinSize = this.getMinimumSize();;
26183         this.activeMaxSize = this.getMaximumSize();;
26184         var c1 = size - this.activeMinSize;
26185         var c2 = Math.max(this.activeMaxSize - size, 0);
26186         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26187             this.dd.resetConstraints();
26188             this.dd.setXConstraint(
26189                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26190                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26191             );
26192             this.dd.setYConstraint(0, 0);
26193         }else{
26194             this.dd.resetConstraints();
26195             this.dd.setXConstraint(0, 0);
26196             this.dd.setYConstraint(
26197                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26198                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26199             );
26200          }
26201         this.dragSpecs.startSize = size;
26202         this.dragSpecs.startPoint = [x, y];
26203         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26204     },
26205     
26206     /** 
26207      * @private Called after the drag operation by the DDProxy
26208      */
26209     onEndProxyDrag : function(e){
26210         Roo.get(this.proxy).setDisplayed(false);
26211         var endPoint = Roo.lib.Event.getXY(e);
26212         if(this.overlay){
26213             this.overlay.hide();
26214         }
26215         var newSize;
26216         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26217             newSize = this.dragSpecs.startSize + 
26218                 (this.placement == Roo.SplitBar.LEFT ?
26219                     endPoint[0] - this.dragSpecs.startPoint[0] :
26220                     this.dragSpecs.startPoint[0] - endPoint[0]
26221                 );
26222         }else{
26223             newSize = this.dragSpecs.startSize + 
26224                 (this.placement == Roo.SplitBar.TOP ?
26225                     endPoint[1] - this.dragSpecs.startPoint[1] :
26226                     this.dragSpecs.startPoint[1] - endPoint[1]
26227                 );
26228         }
26229         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26230         if(newSize != this.dragSpecs.startSize){
26231             if(this.fireEvent('beforeapply', this, newSize) !== false){
26232                 this.adapter.setElementSize(this, newSize);
26233                 this.fireEvent("moved", this, newSize);
26234                 this.fireEvent("resize", this, newSize);
26235             }
26236         }
26237     },
26238     
26239     /**
26240      * Get the adapter this SplitBar uses
26241      * @return The adapter object
26242      */
26243     getAdapter : function(){
26244         return this.adapter;
26245     },
26246     
26247     /**
26248      * Set the adapter this SplitBar uses
26249      * @param {Object} adapter A SplitBar adapter object
26250      */
26251     setAdapter : function(adapter){
26252         this.adapter = adapter;
26253         this.adapter.init(this);
26254     },
26255     
26256     /**
26257      * Gets the minimum size for the resizing element
26258      * @return {Number} The minimum size
26259      */
26260     getMinimumSize : function(){
26261         return this.minSize;
26262     },
26263     
26264     /**
26265      * Sets the minimum size for the resizing element
26266      * @param {Number} minSize The minimum size
26267      */
26268     setMinimumSize : function(minSize){
26269         this.minSize = minSize;
26270     },
26271     
26272     /**
26273      * Gets the maximum size for the resizing element
26274      * @return {Number} The maximum size
26275      */
26276     getMaximumSize : function(){
26277         return this.maxSize;
26278     },
26279     
26280     /**
26281      * Sets the maximum size for the resizing element
26282      * @param {Number} maxSize The maximum size
26283      */
26284     setMaximumSize : function(maxSize){
26285         this.maxSize = maxSize;
26286     },
26287     
26288     /**
26289      * Sets the initialize size for the resizing element
26290      * @param {Number} size The initial size
26291      */
26292     setCurrentSize : function(size){
26293         var oldAnimate = this.animate;
26294         this.animate = false;
26295         this.adapter.setElementSize(this, size);
26296         this.animate = oldAnimate;
26297     },
26298     
26299     /**
26300      * Destroy this splitbar. 
26301      * @param {Boolean} removeEl True to remove the element
26302      */
26303     destroy : function(removeEl){
26304         if(this.shim){
26305             this.shim.remove();
26306         }
26307         this.dd.unreg();
26308         this.proxy.parentNode.removeChild(this.proxy);
26309         if(removeEl){
26310             this.el.remove();
26311         }
26312     }
26313 });
26314
26315 /**
26316  * @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.
26317  */
26318 Roo.SplitBar.createProxy = function(dir){
26319     var proxy = new Roo.Element(document.createElement("div"));
26320     proxy.unselectable();
26321     var cls = 'x-splitbar-proxy';
26322     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26323     document.body.appendChild(proxy.dom);
26324     return proxy.dom;
26325 };
26326
26327 /** 
26328  * @class Roo.SplitBar.BasicLayoutAdapter
26329  * Default Adapter. It assumes the splitter and resizing element are not positioned
26330  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26331  */
26332 Roo.SplitBar.BasicLayoutAdapter = function(){
26333 };
26334
26335 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26336     // do nothing for now
26337     init : function(s){
26338     
26339     },
26340     /**
26341      * Called before drag operations to get the current size of the resizing element. 
26342      * @param {Roo.SplitBar} s The SplitBar using this adapter
26343      */
26344      getElementSize : function(s){
26345         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26346             return s.resizingEl.getWidth();
26347         }else{
26348             return s.resizingEl.getHeight();
26349         }
26350     },
26351     
26352     /**
26353      * Called after drag operations to set the size of the resizing element.
26354      * @param {Roo.SplitBar} s The SplitBar using this adapter
26355      * @param {Number} newSize The new size to set
26356      * @param {Function} onComplete A function to be invoked when resizing is complete
26357      */
26358     setElementSize : function(s, newSize, onComplete){
26359         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26360             if(!s.animate){
26361                 s.resizingEl.setWidth(newSize);
26362                 if(onComplete){
26363                     onComplete(s, newSize);
26364                 }
26365             }else{
26366                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26367             }
26368         }else{
26369             
26370             if(!s.animate){
26371                 s.resizingEl.setHeight(newSize);
26372                 if(onComplete){
26373                     onComplete(s, newSize);
26374                 }
26375             }else{
26376                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26377             }
26378         }
26379     }
26380 };
26381
26382 /** 
26383  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26384  * @extends Roo.SplitBar.BasicLayoutAdapter
26385  * Adapter that  moves the splitter element to align with the resized sizing element. 
26386  * Used with an absolute positioned SplitBar.
26387  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26388  * document.body, make sure you assign an id to the body element.
26389  */
26390 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26391     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26392     this.container = Roo.get(container);
26393 };
26394
26395 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26396     init : function(s){
26397         this.basic.init(s);
26398     },
26399     
26400     getElementSize : function(s){
26401         return this.basic.getElementSize(s);
26402     },
26403     
26404     setElementSize : function(s, newSize, onComplete){
26405         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26406     },
26407     
26408     moveSplitter : function(s){
26409         var yes = Roo.SplitBar;
26410         switch(s.placement){
26411             case yes.LEFT:
26412                 s.el.setX(s.resizingEl.getRight());
26413                 break;
26414             case yes.RIGHT:
26415                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26416                 break;
26417             case yes.TOP:
26418                 s.el.setY(s.resizingEl.getBottom());
26419                 break;
26420             case yes.BOTTOM:
26421                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26422                 break;
26423         }
26424     }
26425 };
26426
26427 /**
26428  * Orientation constant - Create a vertical SplitBar
26429  * @static
26430  * @type Number
26431  */
26432 Roo.SplitBar.VERTICAL = 1;
26433
26434 /**
26435  * Orientation constant - Create a horizontal SplitBar
26436  * @static
26437  * @type Number
26438  */
26439 Roo.SplitBar.HORIZONTAL = 2;
26440
26441 /**
26442  * Placement constant - The resizing element is to the left of the splitter element
26443  * @static
26444  * @type Number
26445  */
26446 Roo.SplitBar.LEFT = 1;
26447
26448 /**
26449  * Placement constant - The resizing element is to the right of the splitter element
26450  * @static
26451  * @type Number
26452  */
26453 Roo.SplitBar.RIGHT = 2;
26454
26455 /**
26456  * Placement constant - The resizing element is positioned above the splitter element
26457  * @static
26458  * @type Number
26459  */
26460 Roo.SplitBar.TOP = 3;
26461
26462 /**
26463  * Placement constant - The resizing element is positioned under splitter element
26464  * @static
26465  * @type Number
26466  */
26467 Roo.SplitBar.BOTTOM = 4;
26468 /*
26469  * Based on:
26470  * Ext JS Library 1.1.1
26471  * Copyright(c) 2006-2007, Ext JS, LLC.
26472  *
26473  * Originally Released Under LGPL - original licence link has changed is not relivant.
26474  *
26475  * Fork - LGPL
26476  * <script type="text/javascript">
26477  */
26478
26479 /**
26480  * @class Roo.View
26481  * @extends Roo.util.Observable
26482  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26483  * This class also supports single and multi selection modes. <br>
26484  * Create a data model bound view:
26485  <pre><code>
26486  var store = new Roo.data.Store(...);
26487
26488  var view = new Roo.View({
26489     el : "my-element",
26490     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26491  
26492     singleSelect: true,
26493     selectedClass: "ydataview-selected",
26494     store: store
26495  });
26496
26497  // listen for node click?
26498  view.on("click", function(vw, index, node, e){
26499  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26500  });
26501
26502  // load XML data
26503  dataModel.load("foobar.xml");
26504  </code></pre>
26505  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26506  * <br><br>
26507  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26508  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26509  * 
26510  * Note: old style constructor is still suported (container, template, config)
26511  * 
26512  * @constructor
26513  * Create a new View
26514  * @param {Object} config The config object
26515  * 
26516  */
26517 Roo.View = function(config, depreciated_tpl, depreciated_config){
26518     
26519     this.parent = false;
26520     
26521     if (typeof(depreciated_tpl) == 'undefined') {
26522         // new way.. - universal constructor.
26523         Roo.apply(this, config);
26524         this.el  = Roo.get(this.el);
26525     } else {
26526         // old format..
26527         this.el  = Roo.get(config);
26528         this.tpl = depreciated_tpl;
26529         Roo.apply(this, depreciated_config);
26530     }
26531     this.wrapEl  = this.el.wrap().wrap();
26532     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26533     
26534     
26535     if(typeof(this.tpl) == "string"){
26536         this.tpl = new Roo.Template(this.tpl);
26537     } else {
26538         // support xtype ctors..
26539         this.tpl = new Roo.factory(this.tpl, Roo);
26540     }
26541     
26542     
26543     this.tpl.compile();
26544     
26545     /** @private */
26546     this.addEvents({
26547         /**
26548          * @event beforeclick
26549          * Fires before a click is processed. Returns false to cancel the default action.
26550          * @param {Roo.View} this
26551          * @param {Number} index The index of the target node
26552          * @param {HTMLElement} node The target node
26553          * @param {Roo.EventObject} e The raw event object
26554          */
26555             "beforeclick" : true,
26556         /**
26557          * @event click
26558          * Fires when a template node is clicked.
26559          * @param {Roo.View} this
26560          * @param {Number} index The index of the target node
26561          * @param {HTMLElement} node The target node
26562          * @param {Roo.EventObject} e The raw event object
26563          */
26564             "click" : true,
26565         /**
26566          * @event dblclick
26567          * Fires when a template node is double clicked.
26568          * @param {Roo.View} this
26569          * @param {Number} index The index of the target node
26570          * @param {HTMLElement} node The target node
26571          * @param {Roo.EventObject} e The raw event object
26572          */
26573             "dblclick" : true,
26574         /**
26575          * @event contextmenu
26576          * Fires when a template node is right clicked.
26577          * @param {Roo.View} this
26578          * @param {Number} index The index of the target node
26579          * @param {HTMLElement} node The target node
26580          * @param {Roo.EventObject} e The raw event object
26581          */
26582             "contextmenu" : true,
26583         /**
26584          * @event selectionchange
26585          * Fires when the selected nodes change.
26586          * @param {Roo.View} this
26587          * @param {Array} selections Array of the selected nodes
26588          */
26589             "selectionchange" : true,
26590     
26591         /**
26592          * @event beforeselect
26593          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26594          * @param {Roo.View} this
26595          * @param {HTMLElement} node The node to be selected
26596          * @param {Array} selections Array of currently selected nodes
26597          */
26598             "beforeselect" : true,
26599         /**
26600          * @event preparedata
26601          * Fires on every row to render, to allow you to change the data.
26602          * @param {Roo.View} this
26603          * @param {Object} data to be rendered (change this)
26604          */
26605           "preparedata" : true
26606           
26607           
26608         });
26609
26610
26611
26612     this.el.on({
26613         "click": this.onClick,
26614         "dblclick": this.onDblClick,
26615         "contextmenu": this.onContextMenu,
26616         scope:this
26617     });
26618
26619     this.selections = [];
26620     this.nodes = [];
26621     this.cmp = new Roo.CompositeElementLite([]);
26622     if(this.store){
26623         this.store = Roo.factory(this.store, Roo.data);
26624         this.setStore(this.store, true);
26625     }
26626     
26627     if ( this.footer && this.footer.xtype) {
26628            
26629          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26630         
26631         this.footer.dataSource = this.store;
26632         this.footer.container = fctr;
26633         this.footer = Roo.factory(this.footer, Roo);
26634         fctr.insertFirst(this.el);
26635         
26636         // this is a bit insane - as the paging toolbar seems to detach the el..
26637 //        dom.parentNode.parentNode.parentNode
26638          // they get detached?
26639     }
26640     
26641     
26642     Roo.View.superclass.constructor.call(this);
26643     
26644     
26645 };
26646
26647 Roo.extend(Roo.View, Roo.util.Observable, {
26648     
26649      /**
26650      * @cfg {Roo.data.Store} store Data store to load data from.
26651      */
26652     store : false,
26653     
26654     /**
26655      * @cfg {String|Roo.Element} el The container element.
26656      */
26657     el : '',
26658     
26659     /**
26660      * @cfg {String|Roo.Template} tpl The template used by this View 
26661      */
26662     tpl : false,
26663     /**
26664      * @cfg {String} dataName the named area of the template to use as the data area
26665      *                          Works with domtemplates roo-name="name"
26666      */
26667     dataName: false,
26668     /**
26669      * @cfg {String} selectedClass The css class to add to selected nodes
26670      */
26671     selectedClass : "x-view-selected",
26672      /**
26673      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26674      */
26675     emptyText : "",
26676     
26677     /**
26678      * @cfg {String} text to display on mask (default Loading)
26679      */
26680     mask : false,
26681     /**
26682      * @cfg {Boolean} multiSelect Allow multiple selection
26683      */
26684     multiSelect : false,
26685     /**
26686      * @cfg {Boolean} singleSelect Allow single selection
26687      */
26688     singleSelect:  false,
26689     
26690     /**
26691      * @cfg {Boolean} toggleSelect - selecting 
26692      */
26693     toggleSelect : false,
26694     
26695     /**
26696      * @cfg {Boolean} tickable - selecting 
26697      */
26698     tickable : false,
26699     
26700     /**
26701      * Returns the element this view is bound to.
26702      * @return {Roo.Element}
26703      */
26704     getEl : function(){
26705         return this.wrapEl;
26706     },
26707     
26708     
26709
26710     /**
26711      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26712      */
26713     refresh : function(){
26714         //Roo.log('refresh');
26715         var t = this.tpl;
26716         
26717         // if we are using something like 'domtemplate', then
26718         // the what gets used is:
26719         // t.applySubtemplate(NAME, data, wrapping data..)
26720         // the outer template then get' applied with
26721         //     the store 'extra data'
26722         // and the body get's added to the
26723         //      roo-name="data" node?
26724         //      <span class='roo-tpl-{name}'></span> ?????
26725         
26726         
26727         
26728         this.clearSelections();
26729         this.el.update("");
26730         var html = [];
26731         var records = this.store.getRange();
26732         if(records.length < 1) {
26733             
26734             // is this valid??  = should it render a template??
26735             
26736             this.el.update(this.emptyText);
26737             return;
26738         }
26739         var el = this.el;
26740         if (this.dataName) {
26741             this.el.update(t.apply(this.store.meta)); //????
26742             el = this.el.child('.roo-tpl-' + this.dataName);
26743         }
26744         
26745         for(var i = 0, len = records.length; i < len; i++){
26746             var data = this.prepareData(records[i].data, i, records[i]);
26747             this.fireEvent("preparedata", this, data, i, records[i]);
26748             
26749             var d = Roo.apply({}, data);
26750             
26751             if(this.tickable){
26752                 Roo.apply(d, {'roo-id' : Roo.id()});
26753                 
26754                 var _this = this;
26755             
26756                 Roo.each(this.parent.item, function(item){
26757                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26758                         return;
26759                     }
26760                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26761                 });
26762             }
26763             
26764             html[html.length] = Roo.util.Format.trim(
26765                 this.dataName ?
26766                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26767                     t.apply(d)
26768             );
26769         }
26770         
26771         
26772         
26773         el.update(html.join(""));
26774         this.nodes = el.dom.childNodes;
26775         this.updateIndexes(0);
26776     },
26777     
26778
26779     /**
26780      * Function to override to reformat the data that is sent to
26781      * the template for each node.
26782      * DEPRICATED - use the preparedata event handler.
26783      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26784      * a JSON object for an UpdateManager bound view).
26785      */
26786     prepareData : function(data, index, record)
26787     {
26788         this.fireEvent("preparedata", this, data, index, record);
26789         return data;
26790     },
26791
26792     onUpdate : function(ds, record){
26793         // Roo.log('on update');   
26794         this.clearSelections();
26795         var index = this.store.indexOf(record);
26796         var n = this.nodes[index];
26797         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26798         n.parentNode.removeChild(n);
26799         this.updateIndexes(index, index);
26800     },
26801
26802     
26803     
26804 // --------- FIXME     
26805     onAdd : function(ds, records, index)
26806     {
26807         //Roo.log(['on Add', ds, records, index] );        
26808         this.clearSelections();
26809         if(this.nodes.length == 0){
26810             this.refresh();
26811             return;
26812         }
26813         var n = this.nodes[index];
26814         for(var i = 0, len = records.length; i < len; i++){
26815             var d = this.prepareData(records[i].data, i, records[i]);
26816             if(n){
26817                 this.tpl.insertBefore(n, d);
26818             }else{
26819                 
26820                 this.tpl.append(this.el, d);
26821             }
26822         }
26823         this.updateIndexes(index);
26824     },
26825
26826     onRemove : function(ds, record, index){
26827        // Roo.log('onRemove');
26828         this.clearSelections();
26829         var el = this.dataName  ?
26830             this.el.child('.roo-tpl-' + this.dataName) :
26831             this.el; 
26832         
26833         el.dom.removeChild(this.nodes[index]);
26834         this.updateIndexes(index);
26835     },
26836
26837     /**
26838      * Refresh an individual node.
26839      * @param {Number} index
26840      */
26841     refreshNode : function(index){
26842         this.onUpdate(this.store, this.store.getAt(index));
26843     },
26844
26845     updateIndexes : function(startIndex, endIndex){
26846         var ns = this.nodes;
26847         startIndex = startIndex || 0;
26848         endIndex = endIndex || ns.length - 1;
26849         for(var i = startIndex; i <= endIndex; i++){
26850             ns[i].nodeIndex = i;
26851         }
26852     },
26853
26854     /**
26855      * Changes the data store this view uses and refresh the view.
26856      * @param {Store} store
26857      */
26858     setStore : function(store, initial){
26859         if(!initial && this.store){
26860             this.store.un("datachanged", this.refresh);
26861             this.store.un("add", this.onAdd);
26862             this.store.un("remove", this.onRemove);
26863             this.store.un("update", this.onUpdate);
26864             this.store.un("clear", this.refresh);
26865             this.store.un("beforeload", this.onBeforeLoad);
26866             this.store.un("load", this.onLoad);
26867             this.store.un("loadexception", this.onLoad);
26868         }
26869         if(store){
26870           
26871             store.on("datachanged", this.refresh, this);
26872             store.on("add", this.onAdd, this);
26873             store.on("remove", this.onRemove, this);
26874             store.on("update", this.onUpdate, this);
26875             store.on("clear", this.refresh, this);
26876             store.on("beforeload", this.onBeforeLoad, this);
26877             store.on("load", this.onLoad, this);
26878             store.on("loadexception", this.onLoad, this);
26879         }
26880         
26881         if(store){
26882             this.refresh();
26883         }
26884     },
26885     /**
26886      * onbeforeLoad - masks the loading area.
26887      *
26888      */
26889     onBeforeLoad : function(store,opts)
26890     {
26891          //Roo.log('onBeforeLoad');   
26892         if (!opts.add) {
26893             this.el.update("");
26894         }
26895         this.el.mask(this.mask ? this.mask : "Loading" ); 
26896     },
26897     onLoad : function ()
26898     {
26899         this.el.unmask();
26900     },
26901     
26902
26903     /**
26904      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26905      * @param {HTMLElement} node
26906      * @return {HTMLElement} The template node
26907      */
26908     findItemFromChild : function(node){
26909         var el = this.dataName  ?
26910             this.el.child('.roo-tpl-' + this.dataName,true) :
26911             this.el.dom; 
26912         
26913         if(!node || node.parentNode == el){
26914                     return node;
26915             }
26916             var p = node.parentNode;
26917             while(p && p != el){
26918             if(p.parentNode == el){
26919                 return p;
26920             }
26921             p = p.parentNode;
26922         }
26923             return null;
26924     },
26925
26926     /** @ignore */
26927     onClick : function(e){
26928         var item = this.findItemFromChild(e.getTarget());
26929         if(item){
26930             var index = this.indexOf(item);
26931             if(this.onItemClick(item, index, e) !== false){
26932                 this.fireEvent("click", this, index, item, e);
26933             }
26934         }else{
26935             this.clearSelections();
26936         }
26937     },
26938
26939     /** @ignore */
26940     onContextMenu : function(e){
26941         var item = this.findItemFromChild(e.getTarget());
26942         if(item){
26943             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26944         }
26945     },
26946
26947     /** @ignore */
26948     onDblClick : function(e){
26949         var item = this.findItemFromChild(e.getTarget());
26950         if(item){
26951             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26952         }
26953     },
26954
26955     onItemClick : function(item, index, e)
26956     {
26957         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26958             return false;
26959         }
26960         if (this.toggleSelect) {
26961             var m = this.isSelected(item) ? 'unselect' : 'select';
26962             //Roo.log(m);
26963             var _t = this;
26964             _t[m](item, true, false);
26965             return true;
26966         }
26967         if(this.multiSelect || this.singleSelect){
26968             if(this.multiSelect && e.shiftKey && this.lastSelection){
26969                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26970             }else{
26971                 this.select(item, this.multiSelect && e.ctrlKey);
26972                 this.lastSelection = item;
26973             }
26974             
26975             if(!this.tickable){
26976                 e.preventDefault();
26977             }
26978             
26979         }
26980         return true;
26981     },
26982
26983     /**
26984      * Get the number of selected nodes.
26985      * @return {Number}
26986      */
26987     getSelectionCount : function(){
26988         return this.selections.length;
26989     },
26990
26991     /**
26992      * Get the currently selected nodes.
26993      * @return {Array} An array of HTMLElements
26994      */
26995     getSelectedNodes : function(){
26996         return this.selections;
26997     },
26998
26999     /**
27000      * Get the indexes of the selected nodes.
27001      * @return {Array}
27002      */
27003     getSelectedIndexes : function(){
27004         var indexes = [], s = this.selections;
27005         for(var i = 0, len = s.length; i < len; i++){
27006             indexes.push(s[i].nodeIndex);
27007         }
27008         return indexes;
27009     },
27010
27011     /**
27012      * Clear all selections
27013      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27014      */
27015     clearSelections : function(suppressEvent){
27016         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27017             this.cmp.elements = this.selections;
27018             this.cmp.removeClass(this.selectedClass);
27019             this.selections = [];
27020             if(!suppressEvent){
27021                 this.fireEvent("selectionchange", this, this.selections);
27022             }
27023         }
27024     },
27025
27026     /**
27027      * Returns true if the passed node is selected
27028      * @param {HTMLElement/Number} node The node or node index
27029      * @return {Boolean}
27030      */
27031     isSelected : function(node){
27032         var s = this.selections;
27033         if(s.length < 1){
27034             return false;
27035         }
27036         node = this.getNode(node);
27037         return s.indexOf(node) !== -1;
27038     },
27039
27040     /**
27041      * Selects nodes.
27042      * @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
27043      * @param {Boolean} keepExisting (optional) true to keep existing selections
27044      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27045      */
27046     select : function(nodeInfo, keepExisting, suppressEvent){
27047         if(nodeInfo instanceof Array){
27048             if(!keepExisting){
27049                 this.clearSelections(true);
27050             }
27051             for(var i = 0, len = nodeInfo.length; i < len; i++){
27052                 this.select(nodeInfo[i], true, true);
27053             }
27054             return;
27055         } 
27056         var node = this.getNode(nodeInfo);
27057         if(!node || this.isSelected(node)){
27058             return; // already selected.
27059         }
27060         if(!keepExisting){
27061             this.clearSelections(true);
27062         }
27063         
27064         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27065             Roo.fly(node).addClass(this.selectedClass);
27066             this.selections.push(node);
27067             if(!suppressEvent){
27068                 this.fireEvent("selectionchange", this, this.selections);
27069             }
27070         }
27071         
27072         
27073     },
27074       /**
27075      * Unselects nodes.
27076      * @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
27077      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27078      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27079      */
27080     unselect : function(nodeInfo, keepExisting, suppressEvent)
27081     {
27082         if(nodeInfo instanceof Array){
27083             Roo.each(this.selections, function(s) {
27084                 this.unselect(s, nodeInfo);
27085             }, this);
27086             return;
27087         }
27088         var node = this.getNode(nodeInfo);
27089         if(!node || !this.isSelected(node)){
27090             //Roo.log("not selected");
27091             return; // not selected.
27092         }
27093         // fireevent???
27094         var ns = [];
27095         Roo.each(this.selections, function(s) {
27096             if (s == node ) {
27097                 Roo.fly(node).removeClass(this.selectedClass);
27098
27099                 return;
27100             }
27101             ns.push(s);
27102         },this);
27103         
27104         this.selections= ns;
27105         this.fireEvent("selectionchange", this, this.selections);
27106     },
27107
27108     /**
27109      * Gets a template node.
27110      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27111      * @return {HTMLElement} The node or null if it wasn't found
27112      */
27113     getNode : function(nodeInfo){
27114         if(typeof nodeInfo == "string"){
27115             return document.getElementById(nodeInfo);
27116         }else if(typeof nodeInfo == "number"){
27117             return this.nodes[nodeInfo];
27118         }
27119         return nodeInfo;
27120     },
27121
27122     /**
27123      * Gets a range template nodes.
27124      * @param {Number} startIndex
27125      * @param {Number} endIndex
27126      * @return {Array} An array of nodes
27127      */
27128     getNodes : function(start, end){
27129         var ns = this.nodes;
27130         start = start || 0;
27131         end = typeof end == "undefined" ? ns.length - 1 : end;
27132         var nodes = [];
27133         if(start <= end){
27134             for(var i = start; i <= end; i++){
27135                 nodes.push(ns[i]);
27136             }
27137         } else{
27138             for(var i = start; i >= end; i--){
27139                 nodes.push(ns[i]);
27140             }
27141         }
27142         return nodes;
27143     },
27144
27145     /**
27146      * Finds the index of the passed node
27147      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27148      * @return {Number} The index of the node or -1
27149      */
27150     indexOf : function(node){
27151         node = this.getNode(node);
27152         if(typeof node.nodeIndex == "number"){
27153             return node.nodeIndex;
27154         }
27155         var ns = this.nodes;
27156         for(var i = 0, len = ns.length; i < len; i++){
27157             if(ns[i] == node){
27158                 return i;
27159             }
27160         }
27161         return -1;
27162     }
27163 });
27164 /*
27165  * Based on:
27166  * Ext JS Library 1.1.1
27167  * Copyright(c) 2006-2007, Ext JS, LLC.
27168  *
27169  * Originally Released Under LGPL - original licence link has changed is not relivant.
27170  *
27171  * Fork - LGPL
27172  * <script type="text/javascript">
27173  */
27174
27175 /**
27176  * @class Roo.JsonView
27177  * @extends Roo.View
27178  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27179 <pre><code>
27180 var view = new Roo.JsonView({
27181     container: "my-element",
27182     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27183     multiSelect: true, 
27184     jsonRoot: "data" 
27185 });
27186
27187 // listen for node click?
27188 view.on("click", function(vw, index, node, e){
27189     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27190 });
27191
27192 // direct load of JSON data
27193 view.load("foobar.php");
27194
27195 // Example from my blog list
27196 var tpl = new Roo.Template(
27197     '&lt;div class="entry"&gt;' +
27198     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27199     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27200     "&lt;/div&gt;&lt;hr /&gt;"
27201 );
27202
27203 var moreView = new Roo.JsonView({
27204     container :  "entry-list", 
27205     template : tpl,
27206     jsonRoot: "posts"
27207 });
27208 moreView.on("beforerender", this.sortEntries, this);
27209 moreView.load({
27210     url: "/blog/get-posts.php",
27211     params: "allposts=true",
27212     text: "Loading Blog Entries..."
27213 });
27214 </code></pre>
27215
27216 * Note: old code is supported with arguments : (container, template, config)
27217
27218
27219  * @constructor
27220  * Create a new JsonView
27221  * 
27222  * @param {Object} config The config object
27223  * 
27224  */
27225 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27226     
27227     
27228     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27229
27230     var um = this.el.getUpdateManager();
27231     um.setRenderer(this);
27232     um.on("update", this.onLoad, this);
27233     um.on("failure", this.onLoadException, this);
27234
27235     /**
27236      * @event beforerender
27237      * Fires before rendering of the downloaded JSON data.
27238      * @param {Roo.JsonView} this
27239      * @param {Object} data The JSON data loaded
27240      */
27241     /**
27242      * @event load
27243      * Fires when data is loaded.
27244      * @param {Roo.JsonView} this
27245      * @param {Object} data The JSON data loaded
27246      * @param {Object} response The raw Connect response object
27247      */
27248     /**
27249      * @event loadexception
27250      * Fires when loading fails.
27251      * @param {Roo.JsonView} this
27252      * @param {Object} response The raw Connect response object
27253      */
27254     this.addEvents({
27255         'beforerender' : true,
27256         'load' : true,
27257         'loadexception' : true
27258     });
27259 };
27260 Roo.extend(Roo.JsonView, Roo.View, {
27261     /**
27262      * @type {String} The root property in the loaded JSON object that contains the data
27263      */
27264     jsonRoot : "",
27265
27266     /**
27267      * Refreshes the view.
27268      */
27269     refresh : function(){
27270         this.clearSelections();
27271         this.el.update("");
27272         var html = [];
27273         var o = this.jsonData;
27274         if(o && o.length > 0){
27275             for(var i = 0, len = o.length; i < len; i++){
27276                 var data = this.prepareData(o[i], i, o);
27277                 html[html.length] = this.tpl.apply(data);
27278             }
27279         }else{
27280             html.push(this.emptyText);
27281         }
27282         this.el.update(html.join(""));
27283         this.nodes = this.el.dom.childNodes;
27284         this.updateIndexes(0);
27285     },
27286
27287     /**
27288      * 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.
27289      * @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:
27290      <pre><code>
27291      view.load({
27292          url: "your-url.php",
27293          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27294          callback: yourFunction,
27295          scope: yourObject, //(optional scope)
27296          discardUrl: false,
27297          nocache: false,
27298          text: "Loading...",
27299          timeout: 30,
27300          scripts: false
27301      });
27302      </code></pre>
27303      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27304      * 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.
27305      * @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}
27306      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27307      * @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.
27308      */
27309     load : function(){
27310         var um = this.el.getUpdateManager();
27311         um.update.apply(um, arguments);
27312     },
27313
27314     // note - render is a standard framework call...
27315     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27316     render : function(el, response){
27317         
27318         this.clearSelections();
27319         this.el.update("");
27320         var o;
27321         try{
27322             if (response != '') {
27323                 o = Roo.util.JSON.decode(response.responseText);
27324                 if(this.jsonRoot){
27325                     
27326                     o = o[this.jsonRoot];
27327                 }
27328             }
27329         } catch(e){
27330         }
27331         /**
27332          * The current JSON data or null
27333          */
27334         this.jsonData = o;
27335         this.beforeRender();
27336         this.refresh();
27337     },
27338
27339 /**
27340  * Get the number of records in the current JSON dataset
27341  * @return {Number}
27342  */
27343     getCount : function(){
27344         return this.jsonData ? this.jsonData.length : 0;
27345     },
27346
27347 /**
27348  * Returns the JSON object for the specified node(s)
27349  * @param {HTMLElement/Array} node The node or an array of nodes
27350  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27351  * you get the JSON object for the node
27352  */
27353     getNodeData : function(node){
27354         if(node instanceof Array){
27355             var data = [];
27356             for(var i = 0, len = node.length; i < len; i++){
27357                 data.push(this.getNodeData(node[i]));
27358             }
27359             return data;
27360         }
27361         return this.jsonData[this.indexOf(node)] || null;
27362     },
27363
27364     beforeRender : function(){
27365         this.snapshot = this.jsonData;
27366         if(this.sortInfo){
27367             this.sort.apply(this, this.sortInfo);
27368         }
27369         this.fireEvent("beforerender", this, this.jsonData);
27370     },
27371
27372     onLoad : function(el, o){
27373         this.fireEvent("load", this, this.jsonData, o);
27374     },
27375
27376     onLoadException : function(el, o){
27377         this.fireEvent("loadexception", this, o);
27378     },
27379
27380 /**
27381  * Filter the data by a specific property.
27382  * @param {String} property A property on your JSON objects
27383  * @param {String/RegExp} value Either string that the property values
27384  * should start with, or a RegExp to test against the property
27385  */
27386     filter : function(property, value){
27387         if(this.jsonData){
27388             var data = [];
27389             var ss = this.snapshot;
27390             if(typeof value == "string"){
27391                 var vlen = value.length;
27392                 if(vlen == 0){
27393                     this.clearFilter();
27394                     return;
27395                 }
27396                 value = value.toLowerCase();
27397                 for(var i = 0, len = ss.length; i < len; i++){
27398                     var o = ss[i];
27399                     if(o[property].substr(0, vlen).toLowerCase() == value){
27400                         data.push(o);
27401                     }
27402                 }
27403             } else if(value.exec){ // regex?
27404                 for(var i = 0, len = ss.length; i < len; i++){
27405                     var o = ss[i];
27406                     if(value.test(o[property])){
27407                         data.push(o);
27408                     }
27409                 }
27410             } else{
27411                 return;
27412             }
27413             this.jsonData = data;
27414             this.refresh();
27415         }
27416     },
27417
27418 /**
27419  * Filter by a function. The passed function will be called with each
27420  * object in the current dataset. If the function returns true the value is kept,
27421  * otherwise it is filtered.
27422  * @param {Function} fn
27423  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27424  */
27425     filterBy : function(fn, scope){
27426         if(this.jsonData){
27427             var data = [];
27428             var ss = this.snapshot;
27429             for(var i = 0, len = ss.length; i < len; i++){
27430                 var o = ss[i];
27431                 if(fn.call(scope || this, o)){
27432                     data.push(o);
27433                 }
27434             }
27435             this.jsonData = data;
27436             this.refresh();
27437         }
27438     },
27439
27440 /**
27441  * Clears the current filter.
27442  */
27443     clearFilter : function(){
27444         if(this.snapshot && this.jsonData != this.snapshot){
27445             this.jsonData = this.snapshot;
27446             this.refresh();
27447         }
27448     },
27449
27450
27451 /**
27452  * Sorts the data for this view and refreshes it.
27453  * @param {String} property A property on your JSON objects to sort on
27454  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27455  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27456  */
27457     sort : function(property, dir, sortType){
27458         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27459         if(this.jsonData){
27460             var p = property;
27461             var dsc = dir && dir.toLowerCase() == "desc";
27462             var f = function(o1, o2){
27463                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27464                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27465                 ;
27466                 if(v1 < v2){
27467                     return dsc ? +1 : -1;
27468                 } else if(v1 > v2){
27469                     return dsc ? -1 : +1;
27470                 } else{
27471                     return 0;
27472                 }
27473             };
27474             this.jsonData.sort(f);
27475             this.refresh();
27476             if(this.jsonData != this.snapshot){
27477                 this.snapshot.sort(f);
27478             }
27479         }
27480     }
27481 });/*
27482  * Based on:
27483  * Ext JS Library 1.1.1
27484  * Copyright(c) 2006-2007, Ext JS, LLC.
27485  *
27486  * Originally Released Under LGPL - original licence link has changed is not relivant.
27487  *
27488  * Fork - LGPL
27489  * <script type="text/javascript">
27490  */
27491  
27492
27493 /**
27494  * @class Roo.ColorPalette
27495  * @extends Roo.Component
27496  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27497  * Here's an example of typical usage:
27498  * <pre><code>
27499 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27500 cp.render('my-div');
27501
27502 cp.on('select', function(palette, selColor){
27503     // do something with selColor
27504 });
27505 </code></pre>
27506  * @constructor
27507  * Create a new ColorPalette
27508  * @param {Object} config The config object
27509  */
27510 Roo.ColorPalette = function(config){
27511     Roo.ColorPalette.superclass.constructor.call(this, config);
27512     this.addEvents({
27513         /**
27514              * @event select
27515              * Fires when a color is selected
27516              * @param {ColorPalette} this
27517              * @param {String} color The 6-digit color hex code (without the # symbol)
27518              */
27519         select: true
27520     });
27521
27522     if(this.handler){
27523         this.on("select", this.handler, this.scope, true);
27524     }
27525 };
27526 Roo.extend(Roo.ColorPalette, Roo.Component, {
27527     /**
27528      * @cfg {String} itemCls
27529      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27530      */
27531     itemCls : "x-color-palette",
27532     /**
27533      * @cfg {String} value
27534      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27535      * the hex codes are case-sensitive.
27536      */
27537     value : null,
27538     clickEvent:'click',
27539     // private
27540     ctype: "Roo.ColorPalette",
27541
27542     /**
27543      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27544      */
27545     allowReselect : false,
27546
27547     /**
27548      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27549      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27550      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27551      * of colors with the width setting until the box is symmetrical.</p>
27552      * <p>You can override individual colors if needed:</p>
27553      * <pre><code>
27554 var cp = new Roo.ColorPalette();
27555 cp.colors[0] = "FF0000";  // change the first box to red
27556 </code></pre>
27557
27558 Or you can provide a custom array of your own for complete control:
27559 <pre><code>
27560 var cp = new Roo.ColorPalette();
27561 cp.colors = ["000000", "993300", "333300"];
27562 </code></pre>
27563      * @type Array
27564      */
27565     colors : [
27566         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27567         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27568         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27569         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27570         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27571     ],
27572
27573     // private
27574     onRender : function(container, position){
27575         var t = new Roo.MasterTemplate(
27576             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27577         );
27578         var c = this.colors;
27579         for(var i = 0, len = c.length; i < len; i++){
27580             t.add([c[i]]);
27581         }
27582         var el = document.createElement("div");
27583         el.className = this.itemCls;
27584         t.overwrite(el);
27585         container.dom.insertBefore(el, position);
27586         this.el = Roo.get(el);
27587         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27588         if(this.clickEvent != 'click'){
27589             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27590         }
27591     },
27592
27593     // private
27594     afterRender : function(){
27595         Roo.ColorPalette.superclass.afterRender.call(this);
27596         if(this.value){
27597             var s = this.value;
27598             this.value = null;
27599             this.select(s);
27600         }
27601     },
27602
27603     // private
27604     handleClick : function(e, t){
27605         e.preventDefault();
27606         if(!this.disabled){
27607             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27608             this.select(c.toUpperCase());
27609         }
27610     },
27611
27612     /**
27613      * Selects the specified color in the palette (fires the select event)
27614      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27615      */
27616     select : function(color){
27617         color = color.replace("#", "");
27618         if(color != this.value || this.allowReselect){
27619             var el = this.el;
27620             if(this.value){
27621                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27622             }
27623             el.child("a.color-"+color).addClass("x-color-palette-sel");
27624             this.value = color;
27625             this.fireEvent("select", this, color);
27626         }
27627     }
27628 });/*
27629  * Based on:
27630  * Ext JS Library 1.1.1
27631  * Copyright(c) 2006-2007, Ext JS, LLC.
27632  *
27633  * Originally Released Under LGPL - original licence link has changed is not relivant.
27634  *
27635  * Fork - LGPL
27636  * <script type="text/javascript">
27637  */
27638  
27639 /**
27640  * @class Roo.DatePicker
27641  * @extends Roo.Component
27642  * Simple date picker class.
27643  * @constructor
27644  * Create a new DatePicker
27645  * @param {Object} config The config object
27646  */
27647 Roo.DatePicker = function(config){
27648     Roo.DatePicker.superclass.constructor.call(this, config);
27649
27650     this.value = config && config.value ?
27651                  config.value.clearTime() : new Date().clearTime();
27652
27653     this.addEvents({
27654         /**
27655              * @event select
27656              * Fires when a date is selected
27657              * @param {DatePicker} this
27658              * @param {Date} date The selected date
27659              */
27660         'select': true,
27661         /**
27662              * @event monthchange
27663              * Fires when the displayed month changes 
27664              * @param {DatePicker} this
27665              * @param {Date} date The selected month
27666              */
27667         'monthchange': true
27668     });
27669
27670     if(this.handler){
27671         this.on("select", this.handler,  this.scope || this);
27672     }
27673     // build the disabledDatesRE
27674     if(!this.disabledDatesRE && this.disabledDates){
27675         var dd = this.disabledDates;
27676         var re = "(?:";
27677         for(var i = 0; i < dd.length; i++){
27678             re += dd[i];
27679             if(i != dd.length-1) {
27680                 re += "|";
27681             }
27682         }
27683         this.disabledDatesRE = new RegExp(re + ")");
27684     }
27685 };
27686
27687 Roo.extend(Roo.DatePicker, Roo.Component, {
27688     /**
27689      * @cfg {String} todayText
27690      * The text to display on the button that selects the current date (defaults to "Today")
27691      */
27692     todayText : "Today",
27693     /**
27694      * @cfg {String} okText
27695      * The text to display on the ok button
27696      */
27697     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27698     /**
27699      * @cfg {String} cancelText
27700      * The text to display on the cancel button
27701      */
27702     cancelText : "Cancel",
27703     /**
27704      * @cfg {String} todayTip
27705      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27706      */
27707     todayTip : "{0} (Spacebar)",
27708     /**
27709      * @cfg {Date} minDate
27710      * Minimum allowable date (JavaScript date object, defaults to null)
27711      */
27712     minDate : null,
27713     /**
27714      * @cfg {Date} maxDate
27715      * Maximum allowable date (JavaScript date object, defaults to null)
27716      */
27717     maxDate : null,
27718     /**
27719      * @cfg {String} minText
27720      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27721      */
27722     minText : "This date is before the minimum date",
27723     /**
27724      * @cfg {String} maxText
27725      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27726      */
27727     maxText : "This date is after the maximum date",
27728     /**
27729      * @cfg {String} format
27730      * The default date format string which can be overriden for localization support.  The format must be
27731      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27732      */
27733     format : "m/d/y",
27734     /**
27735      * @cfg {Array} disabledDays
27736      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27737      */
27738     disabledDays : null,
27739     /**
27740      * @cfg {String} disabledDaysText
27741      * The tooltip to display when the date falls on a disabled day (defaults to "")
27742      */
27743     disabledDaysText : "",
27744     /**
27745      * @cfg {RegExp} disabledDatesRE
27746      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27747      */
27748     disabledDatesRE : null,
27749     /**
27750      * @cfg {String} disabledDatesText
27751      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27752      */
27753     disabledDatesText : "",
27754     /**
27755      * @cfg {Boolean} constrainToViewport
27756      * True to constrain the date picker to the viewport (defaults to true)
27757      */
27758     constrainToViewport : true,
27759     /**
27760      * @cfg {Array} monthNames
27761      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27762      */
27763     monthNames : Date.monthNames,
27764     /**
27765      * @cfg {Array} dayNames
27766      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27767      */
27768     dayNames : Date.dayNames,
27769     /**
27770      * @cfg {String} nextText
27771      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27772      */
27773     nextText: 'Next Month (Control+Right)',
27774     /**
27775      * @cfg {String} prevText
27776      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27777      */
27778     prevText: 'Previous Month (Control+Left)',
27779     /**
27780      * @cfg {String} monthYearText
27781      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27782      */
27783     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27784     /**
27785      * @cfg {Number} startDay
27786      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27787      */
27788     startDay : 0,
27789     /**
27790      * @cfg {Bool} showClear
27791      * Show a clear button (usefull for date form elements that can be blank.)
27792      */
27793     
27794     showClear: false,
27795     
27796     /**
27797      * Sets the value of the date field
27798      * @param {Date} value The date to set
27799      */
27800     setValue : function(value){
27801         var old = this.value;
27802         
27803         if (typeof(value) == 'string') {
27804          
27805             value = Date.parseDate(value, this.format);
27806         }
27807         if (!value) {
27808             value = new Date();
27809         }
27810         
27811         this.value = value.clearTime(true);
27812         if(this.el){
27813             this.update(this.value);
27814         }
27815     },
27816
27817     /**
27818      * Gets the current selected value of the date field
27819      * @return {Date} The selected date
27820      */
27821     getValue : function(){
27822         return this.value;
27823     },
27824
27825     // private
27826     focus : function(){
27827         if(this.el){
27828             this.update(this.activeDate);
27829         }
27830     },
27831
27832     // privateval
27833     onRender : function(container, position){
27834         
27835         var m = [
27836              '<table cellspacing="0">',
27837                 '<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>',
27838                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27839         var dn = this.dayNames;
27840         for(var i = 0; i < 7; i++){
27841             var d = this.startDay+i;
27842             if(d > 6){
27843                 d = d-7;
27844             }
27845             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27846         }
27847         m[m.length] = "</tr></thead><tbody><tr>";
27848         for(var i = 0; i < 42; i++) {
27849             if(i % 7 == 0 && i != 0){
27850                 m[m.length] = "</tr><tr>";
27851             }
27852             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27853         }
27854         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27855             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27856
27857         var el = document.createElement("div");
27858         el.className = "x-date-picker";
27859         el.innerHTML = m.join("");
27860
27861         container.dom.insertBefore(el, position);
27862
27863         this.el = Roo.get(el);
27864         this.eventEl = Roo.get(el.firstChild);
27865
27866         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27867             handler: this.showPrevMonth,
27868             scope: this,
27869             preventDefault:true,
27870             stopDefault:true
27871         });
27872
27873         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27874             handler: this.showNextMonth,
27875             scope: this,
27876             preventDefault:true,
27877             stopDefault:true
27878         });
27879
27880         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27881
27882         this.monthPicker = this.el.down('div.x-date-mp');
27883         this.monthPicker.enableDisplayMode('block');
27884         
27885         var kn = new Roo.KeyNav(this.eventEl, {
27886             "left" : function(e){
27887                 e.ctrlKey ?
27888                     this.showPrevMonth() :
27889                     this.update(this.activeDate.add("d", -1));
27890             },
27891
27892             "right" : function(e){
27893                 e.ctrlKey ?
27894                     this.showNextMonth() :
27895                     this.update(this.activeDate.add("d", 1));
27896             },
27897
27898             "up" : function(e){
27899                 e.ctrlKey ?
27900                     this.showNextYear() :
27901                     this.update(this.activeDate.add("d", -7));
27902             },
27903
27904             "down" : function(e){
27905                 e.ctrlKey ?
27906                     this.showPrevYear() :
27907                     this.update(this.activeDate.add("d", 7));
27908             },
27909
27910             "pageUp" : function(e){
27911                 this.showNextMonth();
27912             },
27913
27914             "pageDown" : function(e){
27915                 this.showPrevMonth();
27916             },
27917
27918             "enter" : function(e){
27919                 e.stopPropagation();
27920                 return true;
27921             },
27922
27923             scope : this
27924         });
27925
27926         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27927
27928         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27929
27930         this.el.unselectable();
27931         
27932         this.cells = this.el.select("table.x-date-inner tbody td");
27933         this.textNodes = this.el.query("table.x-date-inner tbody span");
27934
27935         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27936             text: "&#160;",
27937             tooltip: this.monthYearText
27938         });
27939
27940         this.mbtn.on('click', this.showMonthPicker, this);
27941         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27942
27943
27944         var today = (new Date()).dateFormat(this.format);
27945         
27946         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27947         if (this.showClear) {
27948             baseTb.add( new Roo.Toolbar.Fill());
27949         }
27950         baseTb.add({
27951             text: String.format(this.todayText, today),
27952             tooltip: String.format(this.todayTip, today),
27953             handler: this.selectToday,
27954             scope: this
27955         });
27956         
27957         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27958             
27959         //});
27960         if (this.showClear) {
27961             
27962             baseTb.add( new Roo.Toolbar.Fill());
27963             baseTb.add({
27964                 text: '&#160;',
27965                 cls: 'x-btn-icon x-btn-clear',
27966                 handler: function() {
27967                     //this.value = '';
27968                     this.fireEvent("select", this, '');
27969                 },
27970                 scope: this
27971             });
27972         }
27973         
27974         
27975         if(Roo.isIE){
27976             this.el.repaint();
27977         }
27978         this.update(this.value);
27979     },
27980
27981     createMonthPicker : function(){
27982         if(!this.monthPicker.dom.firstChild){
27983             var buf = ['<table border="0" cellspacing="0">'];
27984             for(var i = 0; i < 6; i++){
27985                 buf.push(
27986                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27987                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27988                     i == 0 ?
27989                     '<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>' :
27990                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27991                 );
27992             }
27993             buf.push(
27994                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27995                     this.okText,
27996                     '</button><button type="button" class="x-date-mp-cancel">',
27997                     this.cancelText,
27998                     '</button></td></tr>',
27999                 '</table>'
28000             );
28001             this.monthPicker.update(buf.join(''));
28002             this.monthPicker.on('click', this.onMonthClick, this);
28003             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28004
28005             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28006             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28007
28008             this.mpMonths.each(function(m, a, i){
28009                 i += 1;
28010                 if((i%2) == 0){
28011                     m.dom.xmonth = 5 + Math.round(i * .5);
28012                 }else{
28013                     m.dom.xmonth = Math.round((i-1) * .5);
28014                 }
28015             });
28016         }
28017     },
28018
28019     showMonthPicker : function(){
28020         this.createMonthPicker();
28021         var size = this.el.getSize();
28022         this.monthPicker.setSize(size);
28023         this.monthPicker.child('table').setSize(size);
28024
28025         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28026         this.updateMPMonth(this.mpSelMonth);
28027         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28028         this.updateMPYear(this.mpSelYear);
28029
28030         this.monthPicker.slideIn('t', {duration:.2});
28031     },
28032
28033     updateMPYear : function(y){
28034         this.mpyear = y;
28035         var ys = this.mpYears.elements;
28036         for(var i = 1; i <= 10; i++){
28037             var td = ys[i-1], y2;
28038             if((i%2) == 0){
28039                 y2 = y + Math.round(i * .5);
28040                 td.firstChild.innerHTML = y2;
28041                 td.xyear = y2;
28042             }else{
28043                 y2 = y - (5-Math.round(i * .5));
28044                 td.firstChild.innerHTML = y2;
28045                 td.xyear = y2;
28046             }
28047             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28048         }
28049     },
28050
28051     updateMPMonth : function(sm){
28052         this.mpMonths.each(function(m, a, i){
28053             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28054         });
28055     },
28056
28057     selectMPMonth: function(m){
28058         
28059     },
28060
28061     onMonthClick : function(e, t){
28062         e.stopEvent();
28063         var el = new Roo.Element(t), pn;
28064         if(el.is('button.x-date-mp-cancel')){
28065             this.hideMonthPicker();
28066         }
28067         else if(el.is('button.x-date-mp-ok')){
28068             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28069             this.hideMonthPicker();
28070         }
28071         else if(pn = el.up('td.x-date-mp-month', 2)){
28072             this.mpMonths.removeClass('x-date-mp-sel');
28073             pn.addClass('x-date-mp-sel');
28074             this.mpSelMonth = pn.dom.xmonth;
28075         }
28076         else if(pn = el.up('td.x-date-mp-year', 2)){
28077             this.mpYears.removeClass('x-date-mp-sel');
28078             pn.addClass('x-date-mp-sel');
28079             this.mpSelYear = pn.dom.xyear;
28080         }
28081         else if(el.is('a.x-date-mp-prev')){
28082             this.updateMPYear(this.mpyear-10);
28083         }
28084         else if(el.is('a.x-date-mp-next')){
28085             this.updateMPYear(this.mpyear+10);
28086         }
28087     },
28088
28089     onMonthDblClick : function(e, t){
28090         e.stopEvent();
28091         var el = new Roo.Element(t), pn;
28092         if(pn = el.up('td.x-date-mp-month', 2)){
28093             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28094             this.hideMonthPicker();
28095         }
28096         else if(pn = el.up('td.x-date-mp-year', 2)){
28097             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28098             this.hideMonthPicker();
28099         }
28100     },
28101
28102     hideMonthPicker : function(disableAnim){
28103         if(this.monthPicker){
28104             if(disableAnim === true){
28105                 this.monthPicker.hide();
28106             }else{
28107                 this.monthPicker.slideOut('t', {duration:.2});
28108             }
28109         }
28110     },
28111
28112     // private
28113     showPrevMonth : function(e){
28114         this.update(this.activeDate.add("mo", -1));
28115     },
28116
28117     // private
28118     showNextMonth : function(e){
28119         this.update(this.activeDate.add("mo", 1));
28120     },
28121
28122     // private
28123     showPrevYear : function(){
28124         this.update(this.activeDate.add("y", -1));
28125     },
28126
28127     // private
28128     showNextYear : function(){
28129         this.update(this.activeDate.add("y", 1));
28130     },
28131
28132     // private
28133     handleMouseWheel : function(e){
28134         var delta = e.getWheelDelta();
28135         if(delta > 0){
28136             this.showPrevMonth();
28137             e.stopEvent();
28138         } else if(delta < 0){
28139             this.showNextMonth();
28140             e.stopEvent();
28141         }
28142     },
28143
28144     // private
28145     handleDateClick : function(e, t){
28146         e.stopEvent();
28147         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28148             this.setValue(new Date(t.dateValue));
28149             this.fireEvent("select", this, this.value);
28150         }
28151     },
28152
28153     // private
28154     selectToday : function(){
28155         this.setValue(new Date().clearTime());
28156         this.fireEvent("select", this, this.value);
28157     },
28158
28159     // private
28160     update : function(date)
28161     {
28162         var vd = this.activeDate;
28163         this.activeDate = date;
28164         if(vd && this.el){
28165             var t = date.getTime();
28166             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28167                 this.cells.removeClass("x-date-selected");
28168                 this.cells.each(function(c){
28169                    if(c.dom.firstChild.dateValue == t){
28170                        c.addClass("x-date-selected");
28171                        setTimeout(function(){
28172                             try{c.dom.firstChild.focus();}catch(e){}
28173                        }, 50);
28174                        return false;
28175                    }
28176                 });
28177                 return;
28178             }
28179         }
28180         
28181         var days = date.getDaysInMonth();
28182         var firstOfMonth = date.getFirstDateOfMonth();
28183         var startingPos = firstOfMonth.getDay()-this.startDay;
28184
28185         if(startingPos <= this.startDay){
28186             startingPos += 7;
28187         }
28188
28189         var pm = date.add("mo", -1);
28190         var prevStart = pm.getDaysInMonth()-startingPos;
28191
28192         var cells = this.cells.elements;
28193         var textEls = this.textNodes;
28194         days += startingPos;
28195
28196         // convert everything to numbers so it's fast
28197         var day = 86400000;
28198         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28199         var today = new Date().clearTime().getTime();
28200         var sel = date.clearTime().getTime();
28201         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28202         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28203         var ddMatch = this.disabledDatesRE;
28204         var ddText = this.disabledDatesText;
28205         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28206         var ddaysText = this.disabledDaysText;
28207         var format = this.format;
28208
28209         var setCellClass = function(cal, cell){
28210             cell.title = "";
28211             var t = d.getTime();
28212             cell.firstChild.dateValue = t;
28213             if(t == today){
28214                 cell.className += " x-date-today";
28215                 cell.title = cal.todayText;
28216             }
28217             if(t == sel){
28218                 cell.className += " x-date-selected";
28219                 setTimeout(function(){
28220                     try{cell.firstChild.focus();}catch(e){}
28221                 }, 50);
28222             }
28223             // disabling
28224             if(t < min) {
28225                 cell.className = " x-date-disabled";
28226                 cell.title = cal.minText;
28227                 return;
28228             }
28229             if(t > max) {
28230                 cell.className = " x-date-disabled";
28231                 cell.title = cal.maxText;
28232                 return;
28233             }
28234             if(ddays){
28235                 if(ddays.indexOf(d.getDay()) != -1){
28236                     cell.title = ddaysText;
28237                     cell.className = " x-date-disabled";
28238                 }
28239             }
28240             if(ddMatch && format){
28241                 var fvalue = d.dateFormat(format);
28242                 if(ddMatch.test(fvalue)){
28243                     cell.title = ddText.replace("%0", fvalue);
28244                     cell.className = " x-date-disabled";
28245                 }
28246             }
28247         };
28248
28249         var i = 0;
28250         for(; i < startingPos; i++) {
28251             textEls[i].innerHTML = (++prevStart);
28252             d.setDate(d.getDate()+1);
28253             cells[i].className = "x-date-prevday";
28254             setCellClass(this, cells[i]);
28255         }
28256         for(; i < days; i++){
28257             intDay = i - startingPos + 1;
28258             textEls[i].innerHTML = (intDay);
28259             d.setDate(d.getDate()+1);
28260             cells[i].className = "x-date-active";
28261             setCellClass(this, cells[i]);
28262         }
28263         var extraDays = 0;
28264         for(; i < 42; i++) {
28265              textEls[i].innerHTML = (++extraDays);
28266              d.setDate(d.getDate()+1);
28267              cells[i].className = "x-date-nextday";
28268              setCellClass(this, cells[i]);
28269         }
28270
28271         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28272         this.fireEvent('monthchange', this, date);
28273         
28274         if(!this.internalRender){
28275             var main = this.el.dom.firstChild;
28276             var w = main.offsetWidth;
28277             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28278             Roo.fly(main).setWidth(w);
28279             this.internalRender = true;
28280             // opera does not respect the auto grow header center column
28281             // then, after it gets a width opera refuses to recalculate
28282             // without a second pass
28283             if(Roo.isOpera && !this.secondPass){
28284                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28285                 this.secondPass = true;
28286                 this.update.defer(10, this, [date]);
28287             }
28288         }
28289         
28290         
28291     }
28292 });        /*
28293  * Based on:
28294  * Ext JS Library 1.1.1
28295  * Copyright(c) 2006-2007, Ext JS, LLC.
28296  *
28297  * Originally Released Under LGPL - original licence link has changed is not relivant.
28298  *
28299  * Fork - LGPL
28300  * <script type="text/javascript">
28301  */
28302 /**
28303  * @class Roo.TabPanel
28304  * @extends Roo.util.Observable
28305  * A lightweight tab container.
28306  * <br><br>
28307  * Usage:
28308  * <pre><code>
28309 // basic tabs 1, built from existing content
28310 var tabs = new Roo.TabPanel("tabs1");
28311 tabs.addTab("script", "View Script");
28312 tabs.addTab("markup", "View Markup");
28313 tabs.activate("script");
28314
28315 // more advanced tabs, built from javascript
28316 var jtabs = new Roo.TabPanel("jtabs");
28317 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28318
28319 // set up the UpdateManager
28320 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28321 var updater = tab2.getUpdateManager();
28322 updater.setDefaultUrl("ajax1.htm");
28323 tab2.on('activate', updater.refresh, updater, true);
28324
28325 // Use setUrl for Ajax loading
28326 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28327 tab3.setUrl("ajax2.htm", null, true);
28328
28329 // Disabled tab
28330 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28331 tab4.disable();
28332
28333 jtabs.activate("jtabs-1");
28334  * </code></pre>
28335  * @constructor
28336  * Create a new TabPanel.
28337  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28338  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28339  */
28340 Roo.TabPanel = function(container, config){
28341     /**
28342     * The container element for this TabPanel.
28343     * @type Roo.Element
28344     */
28345     this.el = Roo.get(container, true);
28346     if(config){
28347         if(typeof config == "boolean"){
28348             this.tabPosition = config ? "bottom" : "top";
28349         }else{
28350             Roo.apply(this, config);
28351         }
28352     }
28353     if(this.tabPosition == "bottom"){
28354         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28355         this.el.addClass("x-tabs-bottom");
28356     }
28357     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28358     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28359     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28360     if(Roo.isIE){
28361         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28362     }
28363     if(this.tabPosition != "bottom"){
28364         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28365          * @type Roo.Element
28366          */
28367         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28368         this.el.addClass("x-tabs-top");
28369     }
28370     this.items = [];
28371
28372     this.bodyEl.setStyle("position", "relative");
28373
28374     this.active = null;
28375     this.activateDelegate = this.activate.createDelegate(this);
28376
28377     this.addEvents({
28378         /**
28379          * @event tabchange
28380          * Fires when the active tab changes
28381          * @param {Roo.TabPanel} this
28382          * @param {Roo.TabPanelItem} activePanel The new active tab
28383          */
28384         "tabchange": true,
28385         /**
28386          * @event beforetabchange
28387          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28388          * @param {Roo.TabPanel} this
28389          * @param {Object} e Set cancel to true on this object to cancel the tab change
28390          * @param {Roo.TabPanelItem} tab The tab being changed to
28391          */
28392         "beforetabchange" : true
28393     });
28394
28395     Roo.EventManager.onWindowResize(this.onResize, this);
28396     this.cpad = this.el.getPadding("lr");
28397     this.hiddenCount = 0;
28398
28399
28400     // toolbar on the tabbar support...
28401     if (this.toolbar) {
28402         var tcfg = this.toolbar;
28403         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28404         this.toolbar = new Roo.Toolbar(tcfg);
28405         if (Roo.isSafari) {
28406             var tbl = tcfg.container.child('table', true);
28407             tbl.setAttribute('width', '100%');
28408         }
28409         
28410     }
28411    
28412
28413
28414     Roo.TabPanel.superclass.constructor.call(this);
28415 };
28416
28417 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28418     /*
28419      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28420      */
28421     tabPosition : "top",
28422     /*
28423      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28424      */
28425     currentTabWidth : 0,
28426     /*
28427      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28428      */
28429     minTabWidth : 40,
28430     /*
28431      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28432      */
28433     maxTabWidth : 250,
28434     /*
28435      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28436      */
28437     preferredTabWidth : 175,
28438     /*
28439      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28440      */
28441     resizeTabs : false,
28442     /*
28443      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28444      */
28445     monitorResize : true,
28446     /*
28447      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28448      */
28449     toolbar : false,
28450
28451     /**
28452      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28453      * @param {String} id The id of the div to use <b>or create</b>
28454      * @param {String} text The text for the tab
28455      * @param {String} content (optional) Content to put in the TabPanelItem body
28456      * @param {Boolean} closable (optional) True to create a close icon on the tab
28457      * @return {Roo.TabPanelItem} The created TabPanelItem
28458      */
28459     addTab : function(id, text, content, closable){
28460         var item = new Roo.TabPanelItem(this, id, text, closable);
28461         this.addTabItem(item);
28462         if(content){
28463             item.setContent(content);
28464         }
28465         return item;
28466     },
28467
28468     /**
28469      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28470      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28471      * @return {Roo.TabPanelItem}
28472      */
28473     getTab : function(id){
28474         return this.items[id];
28475     },
28476
28477     /**
28478      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28479      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28480      */
28481     hideTab : function(id){
28482         var t = this.items[id];
28483         if(!t.isHidden()){
28484            t.setHidden(true);
28485            this.hiddenCount++;
28486            this.autoSizeTabs();
28487         }
28488     },
28489
28490     /**
28491      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28492      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28493      */
28494     unhideTab : function(id){
28495         var t = this.items[id];
28496         if(t.isHidden()){
28497            t.setHidden(false);
28498            this.hiddenCount--;
28499            this.autoSizeTabs();
28500         }
28501     },
28502
28503     /**
28504      * Adds an existing {@link Roo.TabPanelItem}.
28505      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28506      */
28507     addTabItem : function(item){
28508         this.items[item.id] = item;
28509         this.items.push(item);
28510         if(this.resizeTabs){
28511            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28512            this.autoSizeTabs();
28513         }else{
28514             item.autoSize();
28515         }
28516     },
28517
28518     /**
28519      * Removes a {@link Roo.TabPanelItem}.
28520      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28521      */
28522     removeTab : function(id){
28523         var items = this.items;
28524         var tab = items[id];
28525         if(!tab) { return; }
28526         var index = items.indexOf(tab);
28527         if(this.active == tab && items.length > 1){
28528             var newTab = this.getNextAvailable(index);
28529             if(newTab) {
28530                 newTab.activate();
28531             }
28532         }
28533         this.stripEl.dom.removeChild(tab.pnode.dom);
28534         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28535             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28536         }
28537         items.splice(index, 1);
28538         delete this.items[tab.id];
28539         tab.fireEvent("close", tab);
28540         tab.purgeListeners();
28541         this.autoSizeTabs();
28542     },
28543
28544     getNextAvailable : function(start){
28545         var items = this.items;
28546         var index = start;
28547         // look for a next tab that will slide over to
28548         // replace the one being removed
28549         while(index < items.length){
28550             var item = items[++index];
28551             if(item && !item.isHidden()){
28552                 return item;
28553             }
28554         }
28555         // if one isn't found select the previous tab (on the left)
28556         index = start;
28557         while(index >= 0){
28558             var item = items[--index];
28559             if(item && !item.isHidden()){
28560                 return item;
28561             }
28562         }
28563         return null;
28564     },
28565
28566     /**
28567      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28568      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28569      */
28570     disableTab : function(id){
28571         var tab = this.items[id];
28572         if(tab && this.active != tab){
28573             tab.disable();
28574         }
28575     },
28576
28577     /**
28578      * Enables a {@link Roo.TabPanelItem} that is disabled.
28579      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28580      */
28581     enableTab : function(id){
28582         var tab = this.items[id];
28583         tab.enable();
28584     },
28585
28586     /**
28587      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28588      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28589      * @return {Roo.TabPanelItem} The TabPanelItem.
28590      */
28591     activate : function(id){
28592         var tab = this.items[id];
28593         if(!tab){
28594             return null;
28595         }
28596         if(tab == this.active || tab.disabled){
28597             return tab;
28598         }
28599         var e = {};
28600         this.fireEvent("beforetabchange", this, e, tab);
28601         if(e.cancel !== true && !tab.disabled){
28602             if(this.active){
28603                 this.active.hide();
28604             }
28605             this.active = this.items[id];
28606             this.active.show();
28607             this.fireEvent("tabchange", this, this.active);
28608         }
28609         return tab;
28610     },
28611
28612     /**
28613      * Gets the active {@link Roo.TabPanelItem}.
28614      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28615      */
28616     getActiveTab : function(){
28617         return this.active;
28618     },
28619
28620     /**
28621      * Updates the tab body element to fit the height of the container element
28622      * for overflow scrolling
28623      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28624      */
28625     syncHeight : function(targetHeight){
28626         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28627         var bm = this.bodyEl.getMargins();
28628         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28629         this.bodyEl.setHeight(newHeight);
28630         return newHeight;
28631     },
28632
28633     onResize : function(){
28634         if(this.monitorResize){
28635             this.autoSizeTabs();
28636         }
28637     },
28638
28639     /**
28640      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28641      */
28642     beginUpdate : function(){
28643         this.updating = true;
28644     },
28645
28646     /**
28647      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28648      */
28649     endUpdate : function(){
28650         this.updating = false;
28651         this.autoSizeTabs();
28652     },
28653
28654     /**
28655      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28656      */
28657     autoSizeTabs : function(){
28658         var count = this.items.length;
28659         var vcount = count - this.hiddenCount;
28660         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28661             return;
28662         }
28663         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28664         var availWidth = Math.floor(w / vcount);
28665         var b = this.stripBody;
28666         if(b.getWidth() > w){
28667             var tabs = this.items;
28668             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28669             if(availWidth < this.minTabWidth){
28670                 /*if(!this.sleft){    // incomplete scrolling code
28671                     this.createScrollButtons();
28672                 }
28673                 this.showScroll();
28674                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28675             }
28676         }else{
28677             if(this.currentTabWidth < this.preferredTabWidth){
28678                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28679             }
28680         }
28681     },
28682
28683     /**
28684      * Returns the number of tabs in this TabPanel.
28685      * @return {Number}
28686      */
28687      getCount : function(){
28688          return this.items.length;
28689      },
28690
28691     /**
28692      * Resizes all the tabs to the passed width
28693      * @param {Number} The new width
28694      */
28695     setTabWidth : function(width){
28696         this.currentTabWidth = width;
28697         for(var i = 0, len = this.items.length; i < len; i++) {
28698                 if(!this.items[i].isHidden()) {
28699                 this.items[i].setWidth(width);
28700             }
28701         }
28702     },
28703
28704     /**
28705      * Destroys this TabPanel
28706      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28707      */
28708     destroy : function(removeEl){
28709         Roo.EventManager.removeResizeListener(this.onResize, this);
28710         for(var i = 0, len = this.items.length; i < len; i++){
28711             this.items[i].purgeListeners();
28712         }
28713         if(removeEl === true){
28714             this.el.update("");
28715             this.el.remove();
28716         }
28717     }
28718 });
28719
28720 /**
28721  * @class Roo.TabPanelItem
28722  * @extends Roo.util.Observable
28723  * Represents an individual item (tab plus body) in a TabPanel.
28724  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28725  * @param {String} id The id of this TabPanelItem
28726  * @param {String} text The text for the tab of this TabPanelItem
28727  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28728  */
28729 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28730     /**
28731      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28732      * @type Roo.TabPanel
28733      */
28734     this.tabPanel = tabPanel;
28735     /**
28736      * The id for this TabPanelItem
28737      * @type String
28738      */
28739     this.id = id;
28740     /** @private */
28741     this.disabled = false;
28742     /** @private */
28743     this.text = text;
28744     /** @private */
28745     this.loaded = false;
28746     this.closable = closable;
28747
28748     /**
28749      * The body element for this TabPanelItem.
28750      * @type Roo.Element
28751      */
28752     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28753     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28754     this.bodyEl.setStyle("display", "block");
28755     this.bodyEl.setStyle("zoom", "1");
28756     this.hideAction();
28757
28758     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28759     /** @private */
28760     this.el = Roo.get(els.el, true);
28761     this.inner = Roo.get(els.inner, true);
28762     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28763     this.pnode = Roo.get(els.el.parentNode, true);
28764     this.el.on("mousedown", this.onTabMouseDown, this);
28765     this.el.on("click", this.onTabClick, this);
28766     /** @private */
28767     if(closable){
28768         var c = Roo.get(els.close, true);
28769         c.dom.title = this.closeText;
28770         c.addClassOnOver("close-over");
28771         c.on("click", this.closeClick, this);
28772      }
28773
28774     this.addEvents({
28775          /**
28776          * @event activate
28777          * Fires when this tab becomes the active tab.
28778          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28779          * @param {Roo.TabPanelItem} this
28780          */
28781         "activate": true,
28782         /**
28783          * @event beforeclose
28784          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28785          * @param {Roo.TabPanelItem} this
28786          * @param {Object} e Set cancel to true on this object to cancel the close.
28787          */
28788         "beforeclose": true,
28789         /**
28790          * @event close
28791          * Fires when this tab is closed.
28792          * @param {Roo.TabPanelItem} this
28793          */
28794          "close": true,
28795         /**
28796          * @event deactivate
28797          * Fires when this tab is no longer the active tab.
28798          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28799          * @param {Roo.TabPanelItem} this
28800          */
28801          "deactivate" : true
28802     });
28803     this.hidden = false;
28804
28805     Roo.TabPanelItem.superclass.constructor.call(this);
28806 };
28807
28808 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28809     purgeListeners : function(){
28810        Roo.util.Observable.prototype.purgeListeners.call(this);
28811        this.el.removeAllListeners();
28812     },
28813     /**
28814      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28815      */
28816     show : function(){
28817         this.pnode.addClass("on");
28818         this.showAction();
28819         if(Roo.isOpera){
28820             this.tabPanel.stripWrap.repaint();
28821         }
28822         this.fireEvent("activate", this.tabPanel, this);
28823     },
28824
28825     /**
28826      * Returns true if this tab is the active tab.
28827      * @return {Boolean}
28828      */
28829     isActive : function(){
28830         return this.tabPanel.getActiveTab() == this;
28831     },
28832
28833     /**
28834      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28835      */
28836     hide : function(){
28837         this.pnode.removeClass("on");
28838         this.hideAction();
28839         this.fireEvent("deactivate", this.tabPanel, this);
28840     },
28841
28842     hideAction : function(){
28843         this.bodyEl.hide();
28844         this.bodyEl.setStyle("position", "absolute");
28845         this.bodyEl.setLeft("-20000px");
28846         this.bodyEl.setTop("-20000px");
28847     },
28848
28849     showAction : function(){
28850         this.bodyEl.setStyle("position", "relative");
28851         this.bodyEl.setTop("");
28852         this.bodyEl.setLeft("");
28853         this.bodyEl.show();
28854     },
28855
28856     /**
28857      * Set the tooltip for the tab.
28858      * @param {String} tooltip The tab's tooltip
28859      */
28860     setTooltip : function(text){
28861         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28862             this.textEl.dom.qtip = text;
28863             this.textEl.dom.removeAttribute('title');
28864         }else{
28865             this.textEl.dom.title = text;
28866         }
28867     },
28868
28869     onTabClick : function(e){
28870         e.preventDefault();
28871         this.tabPanel.activate(this.id);
28872     },
28873
28874     onTabMouseDown : function(e){
28875         e.preventDefault();
28876         this.tabPanel.activate(this.id);
28877     },
28878
28879     getWidth : function(){
28880         return this.inner.getWidth();
28881     },
28882
28883     setWidth : function(width){
28884         var iwidth = width - this.pnode.getPadding("lr");
28885         this.inner.setWidth(iwidth);
28886         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28887         this.pnode.setWidth(width);
28888     },
28889
28890     /**
28891      * Show or hide the tab
28892      * @param {Boolean} hidden True to hide or false to show.
28893      */
28894     setHidden : function(hidden){
28895         this.hidden = hidden;
28896         this.pnode.setStyle("display", hidden ? "none" : "");
28897     },
28898
28899     /**
28900      * Returns true if this tab is "hidden"
28901      * @return {Boolean}
28902      */
28903     isHidden : function(){
28904         return this.hidden;
28905     },
28906
28907     /**
28908      * Returns the text for this tab
28909      * @return {String}
28910      */
28911     getText : function(){
28912         return this.text;
28913     },
28914
28915     autoSize : function(){
28916         //this.el.beginMeasure();
28917         this.textEl.setWidth(1);
28918         /*
28919          *  #2804 [new] Tabs in Roojs
28920          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28921          */
28922         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28923         //this.el.endMeasure();
28924     },
28925
28926     /**
28927      * Sets the text for the tab (Note: this also sets the tooltip text)
28928      * @param {String} text The tab's text and tooltip
28929      */
28930     setText : function(text){
28931         this.text = text;
28932         this.textEl.update(text);
28933         this.setTooltip(text);
28934         if(!this.tabPanel.resizeTabs){
28935             this.autoSize();
28936         }
28937     },
28938     /**
28939      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28940      */
28941     activate : function(){
28942         this.tabPanel.activate(this.id);
28943     },
28944
28945     /**
28946      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28947      */
28948     disable : function(){
28949         if(this.tabPanel.active != this){
28950             this.disabled = true;
28951             this.pnode.addClass("disabled");
28952         }
28953     },
28954
28955     /**
28956      * Enables this TabPanelItem if it was previously disabled.
28957      */
28958     enable : function(){
28959         this.disabled = false;
28960         this.pnode.removeClass("disabled");
28961     },
28962
28963     /**
28964      * Sets the content for this TabPanelItem.
28965      * @param {String} content The content
28966      * @param {Boolean} loadScripts true to look for and load scripts
28967      */
28968     setContent : function(content, loadScripts){
28969         this.bodyEl.update(content, loadScripts);
28970     },
28971
28972     /**
28973      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28974      * @return {Roo.UpdateManager} The UpdateManager
28975      */
28976     getUpdateManager : function(){
28977         return this.bodyEl.getUpdateManager();
28978     },
28979
28980     /**
28981      * Set a URL to be used to load the content for this TabPanelItem.
28982      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28983      * @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)
28984      * @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)
28985      * @return {Roo.UpdateManager} The UpdateManager
28986      */
28987     setUrl : function(url, params, loadOnce){
28988         if(this.refreshDelegate){
28989             this.un('activate', this.refreshDelegate);
28990         }
28991         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28992         this.on("activate", this.refreshDelegate);
28993         return this.bodyEl.getUpdateManager();
28994     },
28995
28996     /** @private */
28997     _handleRefresh : function(url, params, loadOnce){
28998         if(!loadOnce || !this.loaded){
28999             var updater = this.bodyEl.getUpdateManager();
29000             updater.update(url, params, this._setLoaded.createDelegate(this));
29001         }
29002     },
29003
29004     /**
29005      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29006      *   Will fail silently if the setUrl method has not been called.
29007      *   This does not activate the panel, just updates its content.
29008      */
29009     refresh : function(){
29010         if(this.refreshDelegate){
29011            this.loaded = false;
29012            this.refreshDelegate();
29013         }
29014     },
29015
29016     /** @private */
29017     _setLoaded : function(){
29018         this.loaded = true;
29019     },
29020
29021     /** @private */
29022     closeClick : function(e){
29023         var o = {};
29024         e.stopEvent();
29025         this.fireEvent("beforeclose", this, o);
29026         if(o.cancel !== true){
29027             this.tabPanel.removeTab(this.id);
29028         }
29029     },
29030     /**
29031      * The text displayed in the tooltip for the close icon.
29032      * @type String
29033      */
29034     closeText : "Close this tab"
29035 });
29036
29037 /** @private */
29038 Roo.TabPanel.prototype.createStrip = function(container){
29039     var strip = document.createElement("div");
29040     strip.className = "x-tabs-wrap";
29041     container.appendChild(strip);
29042     return strip;
29043 };
29044 /** @private */
29045 Roo.TabPanel.prototype.createStripList = function(strip){
29046     // div wrapper for retard IE
29047     // returns the "tr" element.
29048     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29049         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29050         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29051     return strip.firstChild.firstChild.firstChild.firstChild;
29052 };
29053 /** @private */
29054 Roo.TabPanel.prototype.createBody = function(container){
29055     var body = document.createElement("div");
29056     Roo.id(body, "tab-body");
29057     Roo.fly(body).addClass("x-tabs-body");
29058     container.appendChild(body);
29059     return body;
29060 };
29061 /** @private */
29062 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29063     var body = Roo.getDom(id);
29064     if(!body){
29065         body = document.createElement("div");
29066         body.id = id;
29067     }
29068     Roo.fly(body).addClass("x-tabs-item-body");
29069     bodyEl.insertBefore(body, bodyEl.firstChild);
29070     return body;
29071 };
29072 /** @private */
29073 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29074     var td = document.createElement("td");
29075     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29076     //stripEl.appendChild(td);
29077     if(closable){
29078         td.className = "x-tabs-closable";
29079         if(!this.closeTpl){
29080             this.closeTpl = new Roo.Template(
29081                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29082                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29083                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29084             );
29085         }
29086         var el = this.closeTpl.overwrite(td, {"text": text});
29087         var close = el.getElementsByTagName("div")[0];
29088         var inner = el.getElementsByTagName("em")[0];
29089         return {"el": el, "close": close, "inner": inner};
29090     } else {
29091         if(!this.tabTpl){
29092             this.tabTpl = new Roo.Template(
29093                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29094                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29095             );
29096         }
29097         var el = this.tabTpl.overwrite(td, {"text": text});
29098         var inner = el.getElementsByTagName("em")[0];
29099         return {"el": el, "inner": inner};
29100     }
29101 };/*
29102  * Based on:
29103  * Ext JS Library 1.1.1
29104  * Copyright(c) 2006-2007, Ext JS, LLC.
29105  *
29106  * Originally Released Under LGPL - original licence link has changed is not relivant.
29107  *
29108  * Fork - LGPL
29109  * <script type="text/javascript">
29110  */
29111
29112 /**
29113  * @class Roo.Button
29114  * @extends Roo.util.Observable
29115  * Simple Button class
29116  * @cfg {String} text The button text
29117  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29118  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29119  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29120  * @cfg {Object} scope The scope of the handler
29121  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29122  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29123  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29124  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29125  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29126  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29127    applies if enableToggle = true)
29128  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29129  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29130   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29131  * @constructor
29132  * Create a new button
29133  * @param {Object} config The config object
29134  */
29135 Roo.Button = function(renderTo, config)
29136 {
29137     if (!config) {
29138         config = renderTo;
29139         renderTo = config.renderTo || false;
29140     }
29141     
29142     Roo.apply(this, config);
29143     this.addEvents({
29144         /**
29145              * @event click
29146              * Fires when this button is clicked
29147              * @param {Button} this
29148              * @param {EventObject} e The click event
29149              */
29150             "click" : true,
29151         /**
29152              * @event toggle
29153              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29154              * @param {Button} this
29155              * @param {Boolean} pressed
29156              */
29157             "toggle" : true,
29158         /**
29159              * @event mouseover
29160              * Fires when the mouse hovers over the button
29161              * @param {Button} this
29162              * @param {Event} e The event object
29163              */
29164         'mouseover' : true,
29165         /**
29166              * @event mouseout
29167              * Fires when the mouse exits the button
29168              * @param {Button} this
29169              * @param {Event} e The event object
29170              */
29171         'mouseout': true,
29172          /**
29173              * @event render
29174              * Fires when the button is rendered
29175              * @param {Button} this
29176              */
29177         'render': true
29178     });
29179     if(this.menu){
29180         this.menu = Roo.menu.MenuMgr.get(this.menu);
29181     }
29182     // register listeners first!!  - so render can be captured..
29183     Roo.util.Observable.call(this);
29184     if(renderTo){
29185         this.render(renderTo);
29186     }
29187     
29188   
29189 };
29190
29191 Roo.extend(Roo.Button, Roo.util.Observable, {
29192     /**
29193      * 
29194      */
29195     
29196     /**
29197      * Read-only. True if this button is hidden
29198      * @type Boolean
29199      */
29200     hidden : false,
29201     /**
29202      * Read-only. True if this button is disabled
29203      * @type Boolean
29204      */
29205     disabled : false,
29206     /**
29207      * Read-only. True if this button is pressed (only if enableToggle = true)
29208      * @type Boolean
29209      */
29210     pressed : false,
29211
29212     /**
29213      * @cfg {Number} tabIndex 
29214      * The DOM tabIndex for this button (defaults to undefined)
29215      */
29216     tabIndex : undefined,
29217
29218     /**
29219      * @cfg {Boolean} enableToggle
29220      * True to enable pressed/not pressed toggling (defaults to false)
29221      */
29222     enableToggle: false,
29223     /**
29224      * @cfg {Mixed} menu
29225      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29226      */
29227     menu : undefined,
29228     /**
29229      * @cfg {String} menuAlign
29230      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29231      */
29232     menuAlign : "tl-bl?",
29233
29234     /**
29235      * @cfg {String} iconCls
29236      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29237      */
29238     iconCls : undefined,
29239     /**
29240      * @cfg {String} type
29241      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29242      */
29243     type : 'button',
29244
29245     // private
29246     menuClassTarget: 'tr',
29247
29248     /**
29249      * @cfg {String} clickEvent
29250      * The type of event to map to the button's event handler (defaults to 'click')
29251      */
29252     clickEvent : 'click',
29253
29254     /**
29255      * @cfg {Boolean} handleMouseEvents
29256      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29257      */
29258     handleMouseEvents : true,
29259
29260     /**
29261      * @cfg {String} tooltipType
29262      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29263      */
29264     tooltipType : 'qtip',
29265
29266     /**
29267      * @cfg {String} cls
29268      * A CSS class to apply to the button's main element.
29269      */
29270     
29271     /**
29272      * @cfg {Roo.Template} template (Optional)
29273      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29274      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29275      * require code modifications if required elements (e.g. a button) aren't present.
29276      */
29277
29278     // private
29279     render : function(renderTo){
29280         var btn;
29281         if(this.hideParent){
29282             this.parentEl = Roo.get(renderTo);
29283         }
29284         if(!this.dhconfig){
29285             if(!this.template){
29286                 if(!Roo.Button.buttonTemplate){
29287                     // hideous table template
29288                     Roo.Button.buttonTemplate = new Roo.Template(
29289                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29290                         '<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>',
29291                         "</tr></tbody></table>");
29292                 }
29293                 this.template = Roo.Button.buttonTemplate;
29294             }
29295             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29296             var btnEl = btn.child("button:first");
29297             btnEl.on('focus', this.onFocus, this);
29298             btnEl.on('blur', this.onBlur, this);
29299             if(this.cls){
29300                 btn.addClass(this.cls);
29301             }
29302             if(this.icon){
29303                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29304             }
29305             if(this.iconCls){
29306                 btnEl.addClass(this.iconCls);
29307                 if(!this.cls){
29308                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29309                 }
29310             }
29311             if(this.tabIndex !== undefined){
29312                 btnEl.dom.tabIndex = this.tabIndex;
29313             }
29314             if(this.tooltip){
29315                 if(typeof this.tooltip == 'object'){
29316                     Roo.QuickTips.tips(Roo.apply({
29317                           target: btnEl.id
29318                     }, this.tooltip));
29319                 } else {
29320                     btnEl.dom[this.tooltipType] = this.tooltip;
29321                 }
29322             }
29323         }else{
29324             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29325         }
29326         this.el = btn;
29327         if(this.id){
29328             this.el.dom.id = this.el.id = this.id;
29329         }
29330         if(this.menu){
29331             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29332             this.menu.on("show", this.onMenuShow, this);
29333             this.menu.on("hide", this.onMenuHide, this);
29334         }
29335         btn.addClass("x-btn");
29336         if(Roo.isIE && !Roo.isIE7){
29337             this.autoWidth.defer(1, this);
29338         }else{
29339             this.autoWidth();
29340         }
29341         if(this.handleMouseEvents){
29342             btn.on("mouseover", this.onMouseOver, this);
29343             btn.on("mouseout", this.onMouseOut, this);
29344             btn.on("mousedown", this.onMouseDown, this);
29345         }
29346         btn.on(this.clickEvent, this.onClick, this);
29347         //btn.on("mouseup", this.onMouseUp, this);
29348         if(this.hidden){
29349             this.hide();
29350         }
29351         if(this.disabled){
29352             this.disable();
29353         }
29354         Roo.ButtonToggleMgr.register(this);
29355         if(this.pressed){
29356             this.el.addClass("x-btn-pressed");
29357         }
29358         if(this.repeat){
29359             var repeater = new Roo.util.ClickRepeater(btn,
29360                 typeof this.repeat == "object" ? this.repeat : {}
29361             );
29362             repeater.on("click", this.onClick,  this);
29363         }
29364         
29365         this.fireEvent('render', this);
29366         
29367     },
29368     /**
29369      * Returns the button's underlying element
29370      * @return {Roo.Element} The element
29371      */
29372     getEl : function(){
29373         return this.el;  
29374     },
29375     
29376     /**
29377      * Destroys this Button and removes any listeners.
29378      */
29379     destroy : function(){
29380         Roo.ButtonToggleMgr.unregister(this);
29381         this.el.removeAllListeners();
29382         this.purgeListeners();
29383         this.el.remove();
29384     },
29385
29386     // private
29387     autoWidth : function(){
29388         if(this.el){
29389             this.el.setWidth("auto");
29390             if(Roo.isIE7 && Roo.isStrict){
29391                 var ib = this.el.child('button');
29392                 if(ib && ib.getWidth() > 20){
29393                     ib.clip();
29394                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29395                 }
29396             }
29397             if(this.minWidth){
29398                 if(this.hidden){
29399                     this.el.beginMeasure();
29400                 }
29401                 if(this.el.getWidth() < this.minWidth){
29402                     this.el.setWidth(this.minWidth);
29403                 }
29404                 if(this.hidden){
29405                     this.el.endMeasure();
29406                 }
29407             }
29408         }
29409     },
29410
29411     /**
29412      * Assigns this button's click handler
29413      * @param {Function} handler The function to call when the button is clicked
29414      * @param {Object} scope (optional) Scope for the function passed in
29415      */
29416     setHandler : function(handler, scope){
29417         this.handler = handler;
29418         this.scope = scope;  
29419     },
29420     
29421     /**
29422      * Sets this button's text
29423      * @param {String} text The button text
29424      */
29425     setText : function(text){
29426         this.text = text;
29427         if(this.el){
29428             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29429         }
29430         this.autoWidth();
29431     },
29432     
29433     /**
29434      * Gets the text for this button
29435      * @return {String} The button text
29436      */
29437     getText : function(){
29438         return this.text;  
29439     },
29440     
29441     /**
29442      * Show this button
29443      */
29444     show: function(){
29445         this.hidden = false;
29446         if(this.el){
29447             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29448         }
29449     },
29450     
29451     /**
29452      * Hide this button
29453      */
29454     hide: function(){
29455         this.hidden = true;
29456         if(this.el){
29457             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29458         }
29459     },
29460     
29461     /**
29462      * Convenience function for boolean show/hide
29463      * @param {Boolean} visible True to show, false to hide
29464      */
29465     setVisible: function(visible){
29466         if(visible) {
29467             this.show();
29468         }else{
29469             this.hide();
29470         }
29471     },
29472     
29473     /**
29474      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29475      * @param {Boolean} state (optional) Force a particular state
29476      */
29477     toggle : function(state){
29478         state = state === undefined ? !this.pressed : state;
29479         if(state != this.pressed){
29480             if(state){
29481                 this.el.addClass("x-btn-pressed");
29482                 this.pressed = true;
29483                 this.fireEvent("toggle", this, true);
29484             }else{
29485                 this.el.removeClass("x-btn-pressed");
29486                 this.pressed = false;
29487                 this.fireEvent("toggle", this, false);
29488             }
29489             if(this.toggleHandler){
29490                 this.toggleHandler.call(this.scope || this, this, state);
29491             }
29492         }
29493     },
29494     
29495     /**
29496      * Focus the button
29497      */
29498     focus : function(){
29499         this.el.child('button:first').focus();
29500     },
29501     
29502     /**
29503      * Disable this button
29504      */
29505     disable : function(){
29506         if(this.el){
29507             this.el.addClass("x-btn-disabled");
29508         }
29509         this.disabled = true;
29510     },
29511     
29512     /**
29513      * Enable this button
29514      */
29515     enable : function(){
29516         if(this.el){
29517             this.el.removeClass("x-btn-disabled");
29518         }
29519         this.disabled = false;
29520     },
29521
29522     /**
29523      * Convenience function for boolean enable/disable
29524      * @param {Boolean} enabled True to enable, false to disable
29525      */
29526     setDisabled : function(v){
29527         this[v !== true ? "enable" : "disable"]();
29528     },
29529
29530     // private
29531     onClick : function(e)
29532     {
29533         if(e){
29534             e.preventDefault();
29535         }
29536         if(e.button != 0){
29537             return;
29538         }
29539         if(!this.disabled){
29540             if(this.enableToggle){
29541                 this.toggle();
29542             }
29543             if(this.menu && !this.menu.isVisible()){
29544                 this.menu.show(this.el, this.menuAlign);
29545             }
29546             this.fireEvent("click", this, e);
29547             if(this.handler){
29548                 this.el.removeClass("x-btn-over");
29549                 this.handler.call(this.scope || this, this, e);
29550             }
29551         }
29552     },
29553     // private
29554     onMouseOver : function(e){
29555         if(!this.disabled){
29556             this.el.addClass("x-btn-over");
29557             this.fireEvent('mouseover', this, e);
29558         }
29559     },
29560     // private
29561     onMouseOut : function(e){
29562         if(!e.within(this.el,  true)){
29563             this.el.removeClass("x-btn-over");
29564             this.fireEvent('mouseout', this, e);
29565         }
29566     },
29567     // private
29568     onFocus : function(e){
29569         if(!this.disabled){
29570             this.el.addClass("x-btn-focus");
29571         }
29572     },
29573     // private
29574     onBlur : function(e){
29575         this.el.removeClass("x-btn-focus");
29576     },
29577     // private
29578     onMouseDown : function(e){
29579         if(!this.disabled && e.button == 0){
29580             this.el.addClass("x-btn-click");
29581             Roo.get(document).on('mouseup', this.onMouseUp, this);
29582         }
29583     },
29584     // private
29585     onMouseUp : function(e){
29586         if(e.button == 0){
29587             this.el.removeClass("x-btn-click");
29588             Roo.get(document).un('mouseup', this.onMouseUp, this);
29589         }
29590     },
29591     // private
29592     onMenuShow : function(e){
29593         this.el.addClass("x-btn-menu-active");
29594     },
29595     // private
29596     onMenuHide : function(e){
29597         this.el.removeClass("x-btn-menu-active");
29598     }   
29599 });
29600
29601 // Private utility class used by Button
29602 Roo.ButtonToggleMgr = function(){
29603    var groups = {};
29604    
29605    function toggleGroup(btn, state){
29606        if(state){
29607            var g = groups[btn.toggleGroup];
29608            for(var i = 0, l = g.length; i < l; i++){
29609                if(g[i] != btn){
29610                    g[i].toggle(false);
29611                }
29612            }
29613        }
29614    }
29615    
29616    return {
29617        register : function(btn){
29618            if(!btn.toggleGroup){
29619                return;
29620            }
29621            var g = groups[btn.toggleGroup];
29622            if(!g){
29623                g = groups[btn.toggleGroup] = [];
29624            }
29625            g.push(btn);
29626            btn.on("toggle", toggleGroup);
29627        },
29628        
29629        unregister : function(btn){
29630            if(!btn.toggleGroup){
29631                return;
29632            }
29633            var g = groups[btn.toggleGroup];
29634            if(g){
29635                g.remove(btn);
29636                btn.un("toggle", toggleGroup);
29637            }
29638        }
29639    };
29640 }();/*
29641  * Based on:
29642  * Ext JS Library 1.1.1
29643  * Copyright(c) 2006-2007, Ext JS, LLC.
29644  *
29645  * Originally Released Under LGPL - original licence link has changed is not relivant.
29646  *
29647  * Fork - LGPL
29648  * <script type="text/javascript">
29649  */
29650  
29651 /**
29652  * @class Roo.SplitButton
29653  * @extends Roo.Button
29654  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29655  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29656  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29657  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29658  * @cfg {String} arrowTooltip The title attribute of the arrow
29659  * @constructor
29660  * Create a new menu button
29661  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29662  * @param {Object} config The config object
29663  */
29664 Roo.SplitButton = function(renderTo, config){
29665     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29666     /**
29667      * @event arrowclick
29668      * Fires when this button's arrow is clicked
29669      * @param {SplitButton} this
29670      * @param {EventObject} e The click event
29671      */
29672     this.addEvents({"arrowclick":true});
29673 };
29674
29675 Roo.extend(Roo.SplitButton, Roo.Button, {
29676     render : function(renderTo){
29677         // this is one sweet looking template!
29678         var tpl = new Roo.Template(
29679             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29680             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29681             '<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>',
29682             "</tbody></table></td><td>",
29683             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29684             '<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>',
29685             "</tbody></table></td></tr></table>"
29686         );
29687         var btn = tpl.append(renderTo, [this.text, this.type], true);
29688         var btnEl = btn.child("button");
29689         if(this.cls){
29690             btn.addClass(this.cls);
29691         }
29692         if(this.icon){
29693             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29694         }
29695         if(this.iconCls){
29696             btnEl.addClass(this.iconCls);
29697             if(!this.cls){
29698                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29699             }
29700         }
29701         this.el = btn;
29702         if(this.handleMouseEvents){
29703             btn.on("mouseover", this.onMouseOver, this);
29704             btn.on("mouseout", this.onMouseOut, this);
29705             btn.on("mousedown", this.onMouseDown, this);
29706             btn.on("mouseup", this.onMouseUp, this);
29707         }
29708         btn.on(this.clickEvent, this.onClick, this);
29709         if(this.tooltip){
29710             if(typeof this.tooltip == 'object'){
29711                 Roo.QuickTips.tips(Roo.apply({
29712                       target: btnEl.id
29713                 }, this.tooltip));
29714             } else {
29715                 btnEl.dom[this.tooltipType] = this.tooltip;
29716             }
29717         }
29718         if(this.arrowTooltip){
29719             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29720         }
29721         if(this.hidden){
29722             this.hide();
29723         }
29724         if(this.disabled){
29725             this.disable();
29726         }
29727         if(this.pressed){
29728             this.el.addClass("x-btn-pressed");
29729         }
29730         if(Roo.isIE && !Roo.isIE7){
29731             this.autoWidth.defer(1, this);
29732         }else{
29733             this.autoWidth();
29734         }
29735         if(this.menu){
29736             this.menu.on("show", this.onMenuShow, this);
29737             this.menu.on("hide", this.onMenuHide, this);
29738         }
29739         this.fireEvent('render', this);
29740     },
29741
29742     // private
29743     autoWidth : function(){
29744         if(this.el){
29745             var tbl = this.el.child("table:first");
29746             var tbl2 = this.el.child("table:last");
29747             this.el.setWidth("auto");
29748             tbl.setWidth("auto");
29749             if(Roo.isIE7 && Roo.isStrict){
29750                 var ib = this.el.child('button:first');
29751                 if(ib && ib.getWidth() > 20){
29752                     ib.clip();
29753                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29754                 }
29755             }
29756             if(this.minWidth){
29757                 if(this.hidden){
29758                     this.el.beginMeasure();
29759                 }
29760                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29761                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29762                 }
29763                 if(this.hidden){
29764                     this.el.endMeasure();
29765                 }
29766             }
29767             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29768         } 
29769     },
29770     /**
29771      * Sets this button's click handler
29772      * @param {Function} handler The function to call when the button is clicked
29773      * @param {Object} scope (optional) Scope for the function passed above
29774      */
29775     setHandler : function(handler, scope){
29776         this.handler = handler;
29777         this.scope = scope;  
29778     },
29779     
29780     /**
29781      * Sets this button's arrow click handler
29782      * @param {Function} handler The function to call when the arrow is clicked
29783      * @param {Object} scope (optional) Scope for the function passed above
29784      */
29785     setArrowHandler : function(handler, scope){
29786         this.arrowHandler = handler;
29787         this.scope = scope;  
29788     },
29789     
29790     /**
29791      * Focus the button
29792      */
29793     focus : function(){
29794         if(this.el){
29795             this.el.child("button:first").focus();
29796         }
29797     },
29798
29799     // private
29800     onClick : function(e){
29801         e.preventDefault();
29802         if(!this.disabled){
29803             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29804                 if(this.menu && !this.menu.isVisible()){
29805                     this.menu.show(this.el, this.menuAlign);
29806                 }
29807                 this.fireEvent("arrowclick", this, e);
29808                 if(this.arrowHandler){
29809                     this.arrowHandler.call(this.scope || this, this, e);
29810                 }
29811             }else{
29812                 this.fireEvent("click", this, e);
29813                 if(this.handler){
29814                     this.handler.call(this.scope || this, this, e);
29815                 }
29816             }
29817         }
29818     },
29819     // private
29820     onMouseDown : function(e){
29821         if(!this.disabled){
29822             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29823         }
29824     },
29825     // private
29826     onMouseUp : function(e){
29827         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29828     }   
29829 });
29830
29831
29832 // backwards compat
29833 Roo.MenuButton = Roo.SplitButton;/*
29834  * Based on:
29835  * Ext JS Library 1.1.1
29836  * Copyright(c) 2006-2007, Ext JS, LLC.
29837  *
29838  * Originally Released Under LGPL - original licence link has changed is not relivant.
29839  *
29840  * Fork - LGPL
29841  * <script type="text/javascript">
29842  */
29843
29844 /**
29845  * @class Roo.Toolbar
29846  * Basic Toolbar class.
29847  * @constructor
29848  * Creates a new Toolbar
29849  * @param {Object} container The config object
29850  */ 
29851 Roo.Toolbar = function(container, buttons, config)
29852 {
29853     /// old consturctor format still supported..
29854     if(container instanceof Array){ // omit the container for later rendering
29855         buttons = container;
29856         config = buttons;
29857         container = null;
29858     }
29859     if (typeof(container) == 'object' && container.xtype) {
29860         config = container;
29861         container = config.container;
29862         buttons = config.buttons || []; // not really - use items!!
29863     }
29864     var xitems = [];
29865     if (config && config.items) {
29866         xitems = config.items;
29867         delete config.items;
29868     }
29869     Roo.apply(this, config);
29870     this.buttons = buttons;
29871     
29872     if(container){
29873         this.render(container);
29874     }
29875     this.xitems = xitems;
29876     Roo.each(xitems, function(b) {
29877         this.add(b);
29878     }, this);
29879     
29880 };
29881
29882 Roo.Toolbar.prototype = {
29883     /**
29884      * @cfg {Array} items
29885      * array of button configs or elements to add (will be converted to a MixedCollection)
29886      */
29887     
29888     /**
29889      * @cfg {String/HTMLElement/Element} container
29890      * The id or element that will contain the toolbar
29891      */
29892     // private
29893     render : function(ct){
29894         this.el = Roo.get(ct);
29895         if(this.cls){
29896             this.el.addClass(this.cls);
29897         }
29898         // using a table allows for vertical alignment
29899         // 100% width is needed by Safari...
29900         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29901         this.tr = this.el.child("tr", true);
29902         var autoId = 0;
29903         this.items = new Roo.util.MixedCollection(false, function(o){
29904             return o.id || ("item" + (++autoId));
29905         });
29906         if(this.buttons){
29907             this.add.apply(this, this.buttons);
29908             delete this.buttons;
29909         }
29910     },
29911
29912     /**
29913      * Adds element(s) to the toolbar -- this function takes a variable number of 
29914      * arguments of mixed type and adds them to the toolbar.
29915      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29916      * <ul>
29917      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29918      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29919      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29920      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29921      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29922      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29923      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29924      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29925      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29926      * </ul>
29927      * @param {Mixed} arg2
29928      * @param {Mixed} etc.
29929      */
29930     add : function(){
29931         var a = arguments, l = a.length;
29932         for(var i = 0; i < l; i++){
29933             this._add(a[i]);
29934         }
29935     },
29936     // private..
29937     _add : function(el) {
29938         
29939         if (el.xtype) {
29940             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29941         }
29942         
29943         if (el.applyTo){ // some kind of form field
29944             return this.addField(el);
29945         } 
29946         if (el.render){ // some kind of Toolbar.Item
29947             return this.addItem(el);
29948         }
29949         if (typeof el == "string"){ // string
29950             if(el == "separator" || el == "-"){
29951                 return this.addSeparator();
29952             }
29953             if (el == " "){
29954                 return this.addSpacer();
29955             }
29956             if(el == "->"){
29957                 return this.addFill();
29958             }
29959             return this.addText(el);
29960             
29961         }
29962         if(el.tagName){ // element
29963             return this.addElement(el);
29964         }
29965         if(typeof el == "object"){ // must be button config?
29966             return this.addButton(el);
29967         }
29968         // and now what?!?!
29969         return false;
29970         
29971     },
29972     
29973     /**
29974      * Add an Xtype element
29975      * @param {Object} xtype Xtype Object
29976      * @return {Object} created Object
29977      */
29978     addxtype : function(e){
29979         return this.add(e);  
29980     },
29981     
29982     /**
29983      * Returns the Element for this toolbar.
29984      * @return {Roo.Element}
29985      */
29986     getEl : function(){
29987         return this.el;  
29988     },
29989     
29990     /**
29991      * Adds a separator
29992      * @return {Roo.Toolbar.Item} The separator item
29993      */
29994     addSeparator : function(){
29995         return this.addItem(new Roo.Toolbar.Separator());
29996     },
29997
29998     /**
29999      * Adds a spacer element
30000      * @return {Roo.Toolbar.Spacer} The spacer item
30001      */
30002     addSpacer : function(){
30003         return this.addItem(new Roo.Toolbar.Spacer());
30004     },
30005
30006     /**
30007      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30008      * @return {Roo.Toolbar.Fill} The fill item
30009      */
30010     addFill : function(){
30011         return this.addItem(new Roo.Toolbar.Fill());
30012     },
30013
30014     /**
30015      * Adds any standard HTML element to the toolbar
30016      * @param {String/HTMLElement/Element} el The element or id of the element to add
30017      * @return {Roo.Toolbar.Item} The element's item
30018      */
30019     addElement : function(el){
30020         return this.addItem(new Roo.Toolbar.Item(el));
30021     },
30022     /**
30023      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30024      * @type Roo.util.MixedCollection  
30025      */
30026     items : false,
30027      
30028     /**
30029      * Adds any Toolbar.Item or subclass
30030      * @param {Roo.Toolbar.Item} item
30031      * @return {Roo.Toolbar.Item} The item
30032      */
30033     addItem : function(item){
30034         var td = this.nextBlock();
30035         item.render(td);
30036         this.items.add(item);
30037         return item;
30038     },
30039     
30040     /**
30041      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30042      * @param {Object/Array} config A button config or array of configs
30043      * @return {Roo.Toolbar.Button/Array}
30044      */
30045     addButton : function(config){
30046         if(config instanceof Array){
30047             var buttons = [];
30048             for(var i = 0, len = config.length; i < len; i++) {
30049                 buttons.push(this.addButton(config[i]));
30050             }
30051             return buttons;
30052         }
30053         var b = config;
30054         if(!(config instanceof Roo.Toolbar.Button)){
30055             b = config.split ?
30056                 new Roo.Toolbar.SplitButton(config) :
30057                 new Roo.Toolbar.Button(config);
30058         }
30059         var td = this.nextBlock();
30060         b.render(td);
30061         this.items.add(b);
30062         return b;
30063     },
30064     
30065     /**
30066      * Adds text to the toolbar
30067      * @param {String} text The text to add
30068      * @return {Roo.Toolbar.Item} The element's item
30069      */
30070     addText : function(text){
30071         return this.addItem(new Roo.Toolbar.TextItem(text));
30072     },
30073     
30074     /**
30075      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30076      * @param {Number} index The index where the item is to be inserted
30077      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30078      * @return {Roo.Toolbar.Button/Item}
30079      */
30080     insertButton : function(index, item){
30081         if(item instanceof Array){
30082             var buttons = [];
30083             for(var i = 0, len = item.length; i < len; i++) {
30084                buttons.push(this.insertButton(index + i, item[i]));
30085             }
30086             return buttons;
30087         }
30088         if (!(item instanceof Roo.Toolbar.Button)){
30089            item = new Roo.Toolbar.Button(item);
30090         }
30091         var td = document.createElement("td");
30092         this.tr.insertBefore(td, this.tr.childNodes[index]);
30093         item.render(td);
30094         this.items.insert(index, item);
30095         return item;
30096     },
30097     
30098     /**
30099      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30100      * @param {Object} config
30101      * @return {Roo.Toolbar.Item} The element's item
30102      */
30103     addDom : function(config, returnEl){
30104         var td = this.nextBlock();
30105         Roo.DomHelper.overwrite(td, config);
30106         var ti = new Roo.Toolbar.Item(td.firstChild);
30107         ti.render(td);
30108         this.items.add(ti);
30109         return ti;
30110     },
30111
30112     /**
30113      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30114      * @type Roo.util.MixedCollection  
30115      */
30116     fields : false,
30117     
30118     /**
30119      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30120      * Note: the field should not have been rendered yet. For a field that has already been
30121      * rendered, use {@link #addElement}.
30122      * @param {Roo.form.Field} field
30123      * @return {Roo.ToolbarItem}
30124      */
30125      
30126       
30127     addField : function(field) {
30128         if (!this.fields) {
30129             var autoId = 0;
30130             this.fields = new Roo.util.MixedCollection(false, function(o){
30131                 return o.id || ("item" + (++autoId));
30132             });
30133
30134         }
30135         
30136         var td = this.nextBlock();
30137         field.render(td);
30138         var ti = new Roo.Toolbar.Item(td.firstChild);
30139         ti.render(td);
30140         this.items.add(ti);
30141         this.fields.add(field);
30142         return ti;
30143     },
30144     /**
30145      * Hide the toolbar
30146      * @method hide
30147      */
30148      
30149       
30150     hide : function()
30151     {
30152         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30153         this.el.child('div').hide();
30154     },
30155     /**
30156      * Show the toolbar
30157      * @method show
30158      */
30159     show : function()
30160     {
30161         this.el.child('div').show();
30162     },
30163       
30164     // private
30165     nextBlock : function(){
30166         var td = document.createElement("td");
30167         this.tr.appendChild(td);
30168         return td;
30169     },
30170
30171     // private
30172     destroy : function(){
30173         if(this.items){ // rendered?
30174             Roo.destroy.apply(Roo, this.items.items);
30175         }
30176         if(this.fields){ // rendered?
30177             Roo.destroy.apply(Roo, this.fields.items);
30178         }
30179         Roo.Element.uncache(this.el, this.tr);
30180     }
30181 };
30182
30183 /**
30184  * @class Roo.Toolbar.Item
30185  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30186  * @constructor
30187  * Creates a new Item
30188  * @param {HTMLElement} el 
30189  */
30190 Roo.Toolbar.Item = function(el){
30191     var cfg = {};
30192     if (typeof (el.xtype) != 'undefined') {
30193         cfg = el;
30194         el = cfg.el;
30195     }
30196     
30197     this.el = Roo.getDom(el);
30198     this.id = Roo.id(this.el);
30199     this.hidden = false;
30200     
30201     this.addEvents({
30202          /**
30203              * @event render
30204              * Fires when the button is rendered
30205              * @param {Button} this
30206              */
30207         'render': true
30208     });
30209     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30210 };
30211 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30212 //Roo.Toolbar.Item.prototype = {
30213     
30214     /**
30215      * Get this item's HTML Element
30216      * @return {HTMLElement}
30217      */
30218     getEl : function(){
30219        return this.el;  
30220     },
30221
30222     // private
30223     render : function(td){
30224         
30225          this.td = td;
30226         td.appendChild(this.el);
30227         
30228         this.fireEvent('render', this);
30229     },
30230     
30231     /**
30232      * Removes and destroys this item.
30233      */
30234     destroy : function(){
30235         this.td.parentNode.removeChild(this.td);
30236     },
30237     
30238     /**
30239      * Shows this item.
30240      */
30241     show: function(){
30242         this.hidden = false;
30243         this.td.style.display = "";
30244     },
30245     
30246     /**
30247      * Hides this item.
30248      */
30249     hide: function(){
30250         this.hidden = true;
30251         this.td.style.display = "none";
30252     },
30253     
30254     /**
30255      * Convenience function for boolean show/hide.
30256      * @param {Boolean} visible true to show/false to hide
30257      */
30258     setVisible: function(visible){
30259         if(visible) {
30260             this.show();
30261         }else{
30262             this.hide();
30263         }
30264     },
30265     
30266     /**
30267      * Try to focus this item.
30268      */
30269     focus : function(){
30270         Roo.fly(this.el).focus();
30271     },
30272     
30273     /**
30274      * Disables this item.
30275      */
30276     disable : function(){
30277         Roo.fly(this.td).addClass("x-item-disabled");
30278         this.disabled = true;
30279         this.el.disabled = true;
30280     },
30281     
30282     /**
30283      * Enables this item.
30284      */
30285     enable : function(){
30286         Roo.fly(this.td).removeClass("x-item-disabled");
30287         this.disabled = false;
30288         this.el.disabled = false;
30289     }
30290 });
30291
30292
30293 /**
30294  * @class Roo.Toolbar.Separator
30295  * @extends Roo.Toolbar.Item
30296  * A simple toolbar separator class
30297  * @constructor
30298  * Creates a new Separator
30299  */
30300 Roo.Toolbar.Separator = function(cfg){
30301     
30302     var s = document.createElement("span");
30303     s.className = "ytb-sep";
30304     if (cfg) {
30305         cfg.el = s;
30306     }
30307     
30308     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30309 };
30310 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30311     enable:Roo.emptyFn,
30312     disable:Roo.emptyFn,
30313     focus:Roo.emptyFn
30314 });
30315
30316 /**
30317  * @class Roo.Toolbar.Spacer
30318  * @extends Roo.Toolbar.Item
30319  * A simple element that adds extra horizontal space to a toolbar.
30320  * @constructor
30321  * Creates a new Spacer
30322  */
30323 Roo.Toolbar.Spacer = function(cfg){
30324     var s = document.createElement("div");
30325     s.className = "ytb-spacer";
30326     if (cfg) {
30327         cfg.el = s;
30328     }
30329     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30330 };
30331 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30332     enable:Roo.emptyFn,
30333     disable:Roo.emptyFn,
30334     focus:Roo.emptyFn
30335 });
30336
30337 /**
30338  * @class Roo.Toolbar.Fill
30339  * @extends Roo.Toolbar.Spacer
30340  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30341  * @constructor
30342  * Creates a new Spacer
30343  */
30344 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30345     // private
30346     render : function(td){
30347         td.style.width = '100%';
30348         Roo.Toolbar.Fill.superclass.render.call(this, td);
30349     }
30350 });
30351
30352 /**
30353  * @class Roo.Toolbar.TextItem
30354  * @extends Roo.Toolbar.Item
30355  * A simple class that renders text directly into a toolbar.
30356  * @constructor
30357  * Creates a new TextItem
30358  * @param {String} text
30359  */
30360 Roo.Toolbar.TextItem = function(cfg){
30361     var  text = cfg || "";
30362     if (typeof(cfg) == 'object') {
30363         text = cfg.text || "";
30364     }  else {
30365         cfg = null;
30366     }
30367     var s = document.createElement("span");
30368     s.className = "ytb-text";
30369     s.innerHTML = text;
30370     if (cfg) {
30371         cfg.el  = s;
30372     }
30373     
30374     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30375 };
30376 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30377     
30378      
30379     enable:Roo.emptyFn,
30380     disable:Roo.emptyFn,
30381     focus:Roo.emptyFn
30382 });
30383
30384 /**
30385  * @class Roo.Toolbar.Button
30386  * @extends Roo.Button
30387  * A button that renders into a toolbar.
30388  * @constructor
30389  * Creates a new Button
30390  * @param {Object} config A standard {@link Roo.Button} config object
30391  */
30392 Roo.Toolbar.Button = function(config){
30393     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30394 };
30395 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30396     render : function(td){
30397         this.td = td;
30398         Roo.Toolbar.Button.superclass.render.call(this, td);
30399     },
30400     
30401     /**
30402      * Removes and destroys this button
30403      */
30404     destroy : function(){
30405         Roo.Toolbar.Button.superclass.destroy.call(this);
30406         this.td.parentNode.removeChild(this.td);
30407     },
30408     
30409     /**
30410      * Shows this button
30411      */
30412     show: function(){
30413         this.hidden = false;
30414         this.td.style.display = "";
30415     },
30416     
30417     /**
30418      * Hides this button
30419      */
30420     hide: function(){
30421         this.hidden = true;
30422         this.td.style.display = "none";
30423     },
30424
30425     /**
30426      * Disables this item
30427      */
30428     disable : function(){
30429         Roo.fly(this.td).addClass("x-item-disabled");
30430         this.disabled = true;
30431     },
30432
30433     /**
30434      * Enables this item
30435      */
30436     enable : function(){
30437         Roo.fly(this.td).removeClass("x-item-disabled");
30438         this.disabled = false;
30439     }
30440 });
30441 // backwards compat
30442 Roo.ToolbarButton = Roo.Toolbar.Button;
30443
30444 /**
30445  * @class Roo.Toolbar.SplitButton
30446  * @extends Roo.SplitButton
30447  * A menu button that renders into a toolbar.
30448  * @constructor
30449  * Creates a new SplitButton
30450  * @param {Object} config A standard {@link Roo.SplitButton} config object
30451  */
30452 Roo.Toolbar.SplitButton = function(config){
30453     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30454 };
30455 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30456     render : function(td){
30457         this.td = td;
30458         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30459     },
30460     
30461     /**
30462      * Removes and destroys this button
30463      */
30464     destroy : function(){
30465         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30466         this.td.parentNode.removeChild(this.td);
30467     },
30468     
30469     /**
30470      * Shows this button
30471      */
30472     show: function(){
30473         this.hidden = false;
30474         this.td.style.display = "";
30475     },
30476     
30477     /**
30478      * Hides this button
30479      */
30480     hide: function(){
30481         this.hidden = true;
30482         this.td.style.display = "none";
30483     }
30484 });
30485
30486 // backwards compat
30487 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30488  * Based on:
30489  * Ext JS Library 1.1.1
30490  * Copyright(c) 2006-2007, Ext JS, LLC.
30491  *
30492  * Originally Released Under LGPL - original licence link has changed is not relivant.
30493  *
30494  * Fork - LGPL
30495  * <script type="text/javascript">
30496  */
30497  
30498 /**
30499  * @class Roo.PagingToolbar
30500  * @extends Roo.Toolbar
30501  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30502  * @constructor
30503  * Create a new PagingToolbar
30504  * @param {Object} config The config object
30505  */
30506 Roo.PagingToolbar = function(el, ds, config)
30507 {
30508     // old args format still supported... - xtype is prefered..
30509     if (typeof(el) == 'object' && el.xtype) {
30510         // created from xtype...
30511         config = el;
30512         ds = el.dataSource;
30513         el = config.container;
30514     }
30515     var items = [];
30516     if (config.items) {
30517         items = config.items;
30518         config.items = [];
30519     }
30520     
30521     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30522     this.ds = ds;
30523     this.cursor = 0;
30524     this.renderButtons(this.el);
30525     this.bind(ds);
30526     
30527     // supprot items array.
30528    
30529     Roo.each(items, function(e) {
30530         this.add(Roo.factory(e));
30531     },this);
30532     
30533 };
30534
30535 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30536     /**
30537      * @cfg {Roo.data.Store} dataSource
30538      * The underlying data store providing the paged data
30539      */
30540     /**
30541      * @cfg {String/HTMLElement/Element} container
30542      * container The id or element that will contain the toolbar
30543      */
30544     /**
30545      * @cfg {Boolean} displayInfo
30546      * True to display the displayMsg (defaults to false)
30547      */
30548     /**
30549      * @cfg {Number} pageSize
30550      * The number of records to display per page (defaults to 20)
30551      */
30552     pageSize: 20,
30553     /**
30554      * @cfg {String} displayMsg
30555      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30556      */
30557     displayMsg : 'Displaying {0} - {1} of {2}',
30558     /**
30559      * @cfg {String} emptyMsg
30560      * The message to display when no records are found (defaults to "No data to display")
30561      */
30562     emptyMsg : 'No data to display',
30563     /**
30564      * Customizable piece of the default paging text (defaults to "Page")
30565      * @type String
30566      */
30567     beforePageText : "Page",
30568     /**
30569      * Customizable piece of the default paging text (defaults to "of %0")
30570      * @type String
30571      */
30572     afterPageText : "of {0}",
30573     /**
30574      * Customizable piece of the default paging text (defaults to "First Page")
30575      * @type String
30576      */
30577     firstText : "First Page",
30578     /**
30579      * Customizable piece of the default paging text (defaults to "Previous Page")
30580      * @type String
30581      */
30582     prevText : "Previous Page",
30583     /**
30584      * Customizable piece of the default paging text (defaults to "Next Page")
30585      * @type String
30586      */
30587     nextText : "Next Page",
30588     /**
30589      * Customizable piece of the default paging text (defaults to "Last Page")
30590      * @type String
30591      */
30592     lastText : "Last Page",
30593     /**
30594      * Customizable piece of the default paging text (defaults to "Refresh")
30595      * @type String
30596      */
30597     refreshText : "Refresh",
30598
30599     // private
30600     renderButtons : function(el){
30601         Roo.PagingToolbar.superclass.render.call(this, el);
30602         this.first = this.addButton({
30603             tooltip: this.firstText,
30604             cls: "x-btn-icon x-grid-page-first",
30605             disabled: true,
30606             handler: this.onClick.createDelegate(this, ["first"])
30607         });
30608         this.prev = this.addButton({
30609             tooltip: this.prevText,
30610             cls: "x-btn-icon x-grid-page-prev",
30611             disabled: true,
30612             handler: this.onClick.createDelegate(this, ["prev"])
30613         });
30614         //this.addSeparator();
30615         this.add(this.beforePageText);
30616         this.field = Roo.get(this.addDom({
30617            tag: "input",
30618            type: "text",
30619            size: "3",
30620            value: "1",
30621            cls: "x-grid-page-number"
30622         }).el);
30623         this.field.on("keydown", this.onPagingKeydown, this);
30624         this.field.on("focus", function(){this.dom.select();});
30625         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30626         this.field.setHeight(18);
30627         //this.addSeparator();
30628         this.next = this.addButton({
30629             tooltip: this.nextText,
30630             cls: "x-btn-icon x-grid-page-next",
30631             disabled: true,
30632             handler: this.onClick.createDelegate(this, ["next"])
30633         });
30634         this.last = this.addButton({
30635             tooltip: this.lastText,
30636             cls: "x-btn-icon x-grid-page-last",
30637             disabled: true,
30638             handler: this.onClick.createDelegate(this, ["last"])
30639         });
30640         //this.addSeparator();
30641         this.loading = this.addButton({
30642             tooltip: this.refreshText,
30643             cls: "x-btn-icon x-grid-loading",
30644             handler: this.onClick.createDelegate(this, ["refresh"])
30645         });
30646
30647         if(this.displayInfo){
30648             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30649         }
30650     },
30651
30652     // private
30653     updateInfo : function(){
30654         if(this.displayEl){
30655             var count = this.ds.getCount();
30656             var msg = count == 0 ?
30657                 this.emptyMsg :
30658                 String.format(
30659                     this.displayMsg,
30660                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30661                 );
30662             this.displayEl.update(msg);
30663         }
30664     },
30665
30666     // private
30667     onLoad : function(ds, r, o){
30668        this.cursor = o.params ? o.params.start : 0;
30669        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30670
30671        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30672        this.field.dom.value = ap;
30673        this.first.setDisabled(ap == 1);
30674        this.prev.setDisabled(ap == 1);
30675        this.next.setDisabled(ap == ps);
30676        this.last.setDisabled(ap == ps);
30677        this.loading.enable();
30678        this.updateInfo();
30679     },
30680
30681     // private
30682     getPageData : function(){
30683         var total = this.ds.getTotalCount();
30684         return {
30685             total : total,
30686             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30687             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30688         };
30689     },
30690
30691     // private
30692     onLoadError : function(){
30693         this.loading.enable();
30694     },
30695
30696     // private
30697     onPagingKeydown : function(e){
30698         var k = e.getKey();
30699         var d = this.getPageData();
30700         if(k == e.RETURN){
30701             var v = this.field.dom.value, pageNum;
30702             if(!v || isNaN(pageNum = parseInt(v, 10))){
30703                 this.field.dom.value = d.activePage;
30704                 return;
30705             }
30706             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30707             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30708             e.stopEvent();
30709         }
30710         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))
30711         {
30712           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30713           this.field.dom.value = pageNum;
30714           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30715           e.stopEvent();
30716         }
30717         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30718         {
30719           var v = this.field.dom.value, pageNum; 
30720           var increment = (e.shiftKey) ? 10 : 1;
30721           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30722             increment *= -1;
30723           }
30724           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30725             this.field.dom.value = d.activePage;
30726             return;
30727           }
30728           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30729           {
30730             this.field.dom.value = parseInt(v, 10) + increment;
30731             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30732             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30733           }
30734           e.stopEvent();
30735         }
30736     },
30737
30738     // private
30739     beforeLoad : function(){
30740         if(this.loading){
30741             this.loading.disable();
30742         }
30743     },
30744
30745     // private
30746     onClick : function(which){
30747         var ds = this.ds;
30748         switch(which){
30749             case "first":
30750                 ds.load({params:{start: 0, limit: this.pageSize}});
30751             break;
30752             case "prev":
30753                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30754             break;
30755             case "next":
30756                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30757             break;
30758             case "last":
30759                 var total = ds.getTotalCount();
30760                 var extra = total % this.pageSize;
30761                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30762                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30763             break;
30764             case "refresh":
30765                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30766             break;
30767         }
30768     },
30769
30770     /**
30771      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30772      * @param {Roo.data.Store} store The data store to unbind
30773      */
30774     unbind : function(ds){
30775         ds.un("beforeload", this.beforeLoad, this);
30776         ds.un("load", this.onLoad, this);
30777         ds.un("loadexception", this.onLoadError, this);
30778         ds.un("remove", this.updateInfo, this);
30779         ds.un("add", this.updateInfo, this);
30780         this.ds = undefined;
30781     },
30782
30783     /**
30784      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30785      * @param {Roo.data.Store} store The data store to bind
30786      */
30787     bind : function(ds){
30788         ds.on("beforeload", this.beforeLoad, this);
30789         ds.on("load", this.onLoad, this);
30790         ds.on("loadexception", this.onLoadError, this);
30791         ds.on("remove", this.updateInfo, this);
30792         ds.on("add", this.updateInfo, this);
30793         this.ds = ds;
30794     }
30795 });/*
30796  * Based on:
30797  * Ext JS Library 1.1.1
30798  * Copyright(c) 2006-2007, Ext JS, LLC.
30799  *
30800  * Originally Released Under LGPL - original licence link has changed is not relivant.
30801  *
30802  * Fork - LGPL
30803  * <script type="text/javascript">
30804  */
30805
30806 /**
30807  * @class Roo.Resizable
30808  * @extends Roo.util.Observable
30809  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30810  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30811  * 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
30812  * the element will be wrapped for you automatically.</p>
30813  * <p>Here is the list of valid resize handles:</p>
30814  * <pre>
30815 Value   Description
30816 ------  -------------------
30817  'n'     north
30818  's'     south
30819  'e'     east
30820  'w'     west
30821  'nw'    northwest
30822  'sw'    southwest
30823  'se'    southeast
30824  'ne'    northeast
30825  'hd'    horizontal drag
30826  'all'   all
30827 </pre>
30828  * <p>Here's an example showing the creation of a typical Resizable:</p>
30829  * <pre><code>
30830 var resizer = new Roo.Resizable("element-id", {
30831     handles: 'all',
30832     minWidth: 200,
30833     minHeight: 100,
30834     maxWidth: 500,
30835     maxHeight: 400,
30836     pinned: true
30837 });
30838 resizer.on("resize", myHandler);
30839 </code></pre>
30840  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30841  * resizer.east.setDisplayed(false);</p>
30842  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30843  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30844  * resize operation's new size (defaults to [0, 0])
30845  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30846  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30847  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30848  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30849  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30850  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30851  * @cfg {Number} width The width of the element in pixels (defaults to null)
30852  * @cfg {Number} height The height of the element in pixels (defaults to null)
30853  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30854  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30855  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30856  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30857  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30858  * in favor of the handles config option (defaults to false)
30859  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30860  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30861  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30862  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30863  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30864  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30865  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30866  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30867  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30868  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30869  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30870  * @constructor
30871  * Create a new resizable component
30872  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30873  * @param {Object} config configuration options
30874   */
30875 Roo.Resizable = function(el, config)
30876 {
30877     this.el = Roo.get(el);
30878
30879     if(config && config.wrap){
30880         config.resizeChild = this.el;
30881         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30882         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30883         this.el.setStyle("overflow", "hidden");
30884         this.el.setPositioning(config.resizeChild.getPositioning());
30885         config.resizeChild.clearPositioning();
30886         if(!config.width || !config.height){
30887             var csize = config.resizeChild.getSize();
30888             this.el.setSize(csize.width, csize.height);
30889         }
30890         if(config.pinned && !config.adjustments){
30891             config.adjustments = "auto";
30892         }
30893     }
30894
30895     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30896     this.proxy.unselectable();
30897     this.proxy.enableDisplayMode('block');
30898
30899     Roo.apply(this, config);
30900
30901     if(this.pinned){
30902         this.disableTrackOver = true;
30903         this.el.addClass("x-resizable-pinned");
30904     }
30905     // if the element isn't positioned, make it relative
30906     var position = this.el.getStyle("position");
30907     if(position != "absolute" && position != "fixed"){
30908         this.el.setStyle("position", "relative");
30909     }
30910     if(!this.handles){ // no handles passed, must be legacy style
30911         this.handles = 's,e,se';
30912         if(this.multiDirectional){
30913             this.handles += ',n,w';
30914         }
30915     }
30916     if(this.handles == "all"){
30917         this.handles = "n s e w ne nw se sw";
30918     }
30919     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30920     var ps = Roo.Resizable.positions;
30921     for(var i = 0, len = hs.length; i < len; i++){
30922         if(hs[i] && ps[hs[i]]){
30923             var pos = ps[hs[i]];
30924             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30925         }
30926     }
30927     // legacy
30928     this.corner = this.southeast;
30929     
30930     // updateBox = the box can move..
30931     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30932         this.updateBox = true;
30933     }
30934
30935     this.activeHandle = null;
30936
30937     if(this.resizeChild){
30938         if(typeof this.resizeChild == "boolean"){
30939             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30940         }else{
30941             this.resizeChild = Roo.get(this.resizeChild, true);
30942         }
30943     }
30944     
30945     if(this.adjustments == "auto"){
30946         var rc = this.resizeChild;
30947         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30948         if(rc && (hw || hn)){
30949             rc.position("relative");
30950             rc.setLeft(hw ? hw.el.getWidth() : 0);
30951             rc.setTop(hn ? hn.el.getHeight() : 0);
30952         }
30953         this.adjustments = [
30954             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30955             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30956         ];
30957     }
30958
30959     if(this.draggable){
30960         this.dd = this.dynamic ?
30961             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30962         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30963     }
30964
30965     // public events
30966     this.addEvents({
30967         /**
30968          * @event beforeresize
30969          * Fired before resize is allowed. Set enabled to false to cancel resize.
30970          * @param {Roo.Resizable} this
30971          * @param {Roo.EventObject} e The mousedown event
30972          */
30973         "beforeresize" : true,
30974         /**
30975          * @event resizing
30976          * Fired a resizing.
30977          * @param {Roo.Resizable} this
30978          * @param {Number} x The new x position
30979          * @param {Number} y The new y position
30980          * @param {Number} w The new w width
30981          * @param {Number} h The new h hight
30982          * @param {Roo.EventObject} e The mouseup event
30983          */
30984         "resizing" : true,
30985         /**
30986          * @event resize
30987          * Fired after a resize.
30988          * @param {Roo.Resizable} this
30989          * @param {Number} width The new width
30990          * @param {Number} height The new height
30991          * @param {Roo.EventObject} e The mouseup event
30992          */
30993         "resize" : true
30994     });
30995
30996     if(this.width !== null && this.height !== null){
30997         this.resizeTo(this.width, this.height);
30998     }else{
30999         this.updateChildSize();
31000     }
31001     if(Roo.isIE){
31002         this.el.dom.style.zoom = 1;
31003     }
31004     Roo.Resizable.superclass.constructor.call(this);
31005 };
31006
31007 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31008         resizeChild : false,
31009         adjustments : [0, 0],
31010         minWidth : 5,
31011         minHeight : 5,
31012         maxWidth : 10000,
31013         maxHeight : 10000,
31014         enabled : true,
31015         animate : false,
31016         duration : .35,
31017         dynamic : false,
31018         handles : false,
31019         multiDirectional : false,
31020         disableTrackOver : false,
31021         easing : 'easeOutStrong',
31022         widthIncrement : 0,
31023         heightIncrement : 0,
31024         pinned : false,
31025         width : null,
31026         height : null,
31027         preserveRatio : false,
31028         transparent: false,
31029         minX: 0,
31030         minY: 0,
31031         draggable: false,
31032
31033         /**
31034          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31035          */
31036         constrainTo: undefined,
31037         /**
31038          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31039          */
31040         resizeRegion: undefined,
31041
31042
31043     /**
31044      * Perform a manual resize
31045      * @param {Number} width
31046      * @param {Number} height
31047      */
31048     resizeTo : function(width, height){
31049         this.el.setSize(width, height);
31050         this.updateChildSize();
31051         this.fireEvent("resize", this, width, height, null);
31052     },
31053
31054     // private
31055     startSizing : function(e, handle){
31056         this.fireEvent("beforeresize", this, e);
31057         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31058
31059             if(!this.overlay){
31060                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31061                 this.overlay.unselectable();
31062                 this.overlay.enableDisplayMode("block");
31063                 this.overlay.on("mousemove", this.onMouseMove, this);
31064                 this.overlay.on("mouseup", this.onMouseUp, this);
31065             }
31066             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31067
31068             this.resizing = true;
31069             this.startBox = this.el.getBox();
31070             this.startPoint = e.getXY();
31071             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31072                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31073
31074             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31075             this.overlay.show();
31076
31077             if(this.constrainTo) {
31078                 var ct = Roo.get(this.constrainTo);
31079                 this.resizeRegion = ct.getRegion().adjust(
31080                     ct.getFrameWidth('t'),
31081                     ct.getFrameWidth('l'),
31082                     -ct.getFrameWidth('b'),
31083                     -ct.getFrameWidth('r')
31084                 );
31085             }
31086
31087             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31088             this.proxy.show();
31089             this.proxy.setBox(this.startBox);
31090             if(!this.dynamic){
31091                 this.proxy.setStyle('visibility', 'visible');
31092             }
31093         }
31094     },
31095
31096     // private
31097     onMouseDown : function(handle, e){
31098         if(this.enabled){
31099             e.stopEvent();
31100             this.activeHandle = handle;
31101             this.startSizing(e, handle);
31102         }
31103     },
31104
31105     // private
31106     onMouseUp : function(e){
31107         var size = this.resizeElement();
31108         this.resizing = false;
31109         this.handleOut();
31110         this.overlay.hide();
31111         this.proxy.hide();
31112         this.fireEvent("resize", this, size.width, size.height, e);
31113     },
31114
31115     // private
31116     updateChildSize : function(){
31117         
31118         if(this.resizeChild){
31119             var el = this.el;
31120             var child = this.resizeChild;
31121             var adj = this.adjustments;
31122             if(el.dom.offsetWidth){
31123                 var b = el.getSize(true);
31124                 child.setSize(b.width+adj[0], b.height+adj[1]);
31125             }
31126             // Second call here for IE
31127             // The first call enables instant resizing and
31128             // the second call corrects scroll bars if they
31129             // exist
31130             if(Roo.isIE){
31131                 setTimeout(function(){
31132                     if(el.dom.offsetWidth){
31133                         var b = el.getSize(true);
31134                         child.setSize(b.width+adj[0], b.height+adj[1]);
31135                     }
31136                 }, 10);
31137             }
31138         }
31139     },
31140
31141     // private
31142     snap : function(value, inc, min){
31143         if(!inc || !value) {
31144             return value;
31145         }
31146         var newValue = value;
31147         var m = value % inc;
31148         if(m > 0){
31149             if(m > (inc/2)){
31150                 newValue = value + (inc-m);
31151             }else{
31152                 newValue = value - m;
31153             }
31154         }
31155         return Math.max(min, newValue);
31156     },
31157
31158     // private
31159     resizeElement : function(){
31160         var box = this.proxy.getBox();
31161         if(this.updateBox){
31162             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31163         }else{
31164             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31165         }
31166         this.updateChildSize();
31167         if(!this.dynamic){
31168             this.proxy.hide();
31169         }
31170         return box;
31171     },
31172
31173     // private
31174     constrain : function(v, diff, m, mx){
31175         if(v - diff < m){
31176             diff = v - m;
31177         }else if(v - diff > mx){
31178             diff = mx - v;
31179         }
31180         return diff;
31181     },
31182
31183     // private
31184     onMouseMove : function(e){
31185         
31186         if(this.enabled){
31187             try{// try catch so if something goes wrong the user doesn't get hung
31188
31189             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31190                 return;
31191             }
31192
31193             //var curXY = this.startPoint;
31194             var curSize = this.curSize || this.startBox;
31195             var x = this.startBox.x, y = this.startBox.y;
31196             var ox = x, oy = y;
31197             var w = curSize.width, h = curSize.height;
31198             var ow = w, oh = h;
31199             var mw = this.minWidth, mh = this.minHeight;
31200             var mxw = this.maxWidth, mxh = this.maxHeight;
31201             var wi = this.widthIncrement;
31202             var hi = this.heightIncrement;
31203
31204             var eventXY = e.getXY();
31205             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31206             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31207
31208             var pos = this.activeHandle.position;
31209
31210             switch(pos){
31211                 case "east":
31212                     w += diffX;
31213                     w = Math.min(Math.max(mw, w), mxw);
31214                     break;
31215              
31216                 case "south":
31217                     h += diffY;
31218                     h = Math.min(Math.max(mh, h), mxh);
31219                     break;
31220                 case "southeast":
31221                     w += diffX;
31222                     h += diffY;
31223                     w = Math.min(Math.max(mw, w), mxw);
31224                     h = Math.min(Math.max(mh, h), mxh);
31225                     break;
31226                 case "north":
31227                     diffY = this.constrain(h, diffY, mh, mxh);
31228                     y += diffY;
31229                     h -= diffY;
31230                     break;
31231                 case "hdrag":
31232                     
31233                     if (wi) {
31234                         var adiffX = Math.abs(diffX);
31235                         var sub = (adiffX % wi); // how much 
31236                         if (sub > (wi/2)) { // far enough to snap
31237                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31238                         } else {
31239                             // remove difference.. 
31240                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31241                         }
31242                     }
31243                     x += diffX;
31244                     x = Math.max(this.minX, x);
31245                     break;
31246                 case "west":
31247                     diffX = this.constrain(w, diffX, mw, mxw);
31248                     x += diffX;
31249                     w -= diffX;
31250                     break;
31251                 case "northeast":
31252                     w += diffX;
31253                     w = Math.min(Math.max(mw, w), mxw);
31254                     diffY = this.constrain(h, diffY, mh, mxh);
31255                     y += diffY;
31256                     h -= diffY;
31257                     break;
31258                 case "northwest":
31259                     diffX = this.constrain(w, diffX, mw, mxw);
31260                     diffY = this.constrain(h, diffY, mh, mxh);
31261                     y += diffY;
31262                     h -= diffY;
31263                     x += diffX;
31264                     w -= diffX;
31265                     break;
31266                case "southwest":
31267                     diffX = this.constrain(w, diffX, mw, mxw);
31268                     h += diffY;
31269                     h = Math.min(Math.max(mh, h), mxh);
31270                     x += diffX;
31271                     w -= diffX;
31272                     break;
31273             }
31274
31275             var sw = this.snap(w, wi, mw);
31276             var sh = this.snap(h, hi, mh);
31277             if(sw != w || sh != h){
31278                 switch(pos){
31279                     case "northeast":
31280                         y -= sh - h;
31281                     break;
31282                     case "north":
31283                         y -= sh - h;
31284                         break;
31285                     case "southwest":
31286                         x -= sw - w;
31287                     break;
31288                     case "west":
31289                         x -= sw - w;
31290                         break;
31291                     case "northwest":
31292                         x -= sw - w;
31293                         y -= sh - h;
31294                     break;
31295                 }
31296                 w = sw;
31297                 h = sh;
31298             }
31299
31300             if(this.preserveRatio){
31301                 switch(pos){
31302                     case "southeast":
31303                     case "east":
31304                         h = oh * (w/ow);
31305                         h = Math.min(Math.max(mh, h), mxh);
31306                         w = ow * (h/oh);
31307                        break;
31308                     case "south":
31309                         w = ow * (h/oh);
31310                         w = Math.min(Math.max(mw, w), mxw);
31311                         h = oh * (w/ow);
31312                         break;
31313                     case "northeast":
31314                         w = ow * (h/oh);
31315                         w = Math.min(Math.max(mw, w), mxw);
31316                         h = oh * (w/ow);
31317                     break;
31318                     case "north":
31319                         var tw = w;
31320                         w = ow * (h/oh);
31321                         w = Math.min(Math.max(mw, w), mxw);
31322                         h = oh * (w/ow);
31323                         x += (tw - w) / 2;
31324                         break;
31325                     case "southwest":
31326                         h = oh * (w/ow);
31327                         h = Math.min(Math.max(mh, h), mxh);
31328                         var tw = w;
31329                         w = ow * (h/oh);
31330                         x += tw - w;
31331                         break;
31332                     case "west":
31333                         var th = h;
31334                         h = oh * (w/ow);
31335                         h = Math.min(Math.max(mh, h), mxh);
31336                         y += (th - h) / 2;
31337                         var tw = w;
31338                         w = ow * (h/oh);
31339                         x += tw - w;
31340                        break;
31341                     case "northwest":
31342                         var tw = w;
31343                         var th = h;
31344                         h = oh * (w/ow);
31345                         h = Math.min(Math.max(mh, h), mxh);
31346                         w = ow * (h/oh);
31347                         y += th - h;
31348                         x += tw - w;
31349                        break;
31350
31351                 }
31352             }
31353             if (pos == 'hdrag') {
31354                 w = ow;
31355             }
31356             this.proxy.setBounds(x, y, w, h);
31357             if(this.dynamic){
31358                 this.resizeElement();
31359             }
31360             }catch(e){}
31361         }
31362         this.fireEvent("resizing", this, x, y, w, h, e);
31363     },
31364
31365     // private
31366     handleOver : function(){
31367         if(this.enabled){
31368             this.el.addClass("x-resizable-over");
31369         }
31370     },
31371
31372     // private
31373     handleOut : function(){
31374         if(!this.resizing){
31375             this.el.removeClass("x-resizable-over");
31376         }
31377     },
31378
31379     /**
31380      * Returns the element this component is bound to.
31381      * @return {Roo.Element}
31382      */
31383     getEl : function(){
31384         return this.el;
31385     },
31386
31387     /**
31388      * Returns the resizeChild element (or null).
31389      * @return {Roo.Element}
31390      */
31391     getResizeChild : function(){
31392         return this.resizeChild;
31393     },
31394     groupHandler : function()
31395     {
31396         
31397     },
31398     /**
31399      * Destroys this resizable. If the element was wrapped and
31400      * removeEl is not true then the element remains.
31401      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31402      */
31403     destroy : function(removeEl){
31404         this.proxy.remove();
31405         if(this.overlay){
31406             this.overlay.removeAllListeners();
31407             this.overlay.remove();
31408         }
31409         var ps = Roo.Resizable.positions;
31410         for(var k in ps){
31411             if(typeof ps[k] != "function" && this[ps[k]]){
31412                 var h = this[ps[k]];
31413                 h.el.removeAllListeners();
31414                 h.el.remove();
31415             }
31416         }
31417         if(removeEl){
31418             this.el.update("");
31419             this.el.remove();
31420         }
31421     }
31422 });
31423
31424 // private
31425 // hash to map config positions to true positions
31426 Roo.Resizable.positions = {
31427     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31428     hd: "hdrag"
31429 };
31430
31431 // private
31432 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31433     if(!this.tpl){
31434         // only initialize the template if resizable is used
31435         var tpl = Roo.DomHelper.createTemplate(
31436             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31437         );
31438         tpl.compile();
31439         Roo.Resizable.Handle.prototype.tpl = tpl;
31440     }
31441     this.position = pos;
31442     this.rz = rz;
31443     // show north drag fro topdra
31444     var handlepos = pos == 'hdrag' ? 'north' : pos;
31445     
31446     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31447     if (pos == 'hdrag') {
31448         this.el.setStyle('cursor', 'pointer');
31449     }
31450     this.el.unselectable();
31451     if(transparent){
31452         this.el.setOpacity(0);
31453     }
31454     this.el.on("mousedown", this.onMouseDown, this);
31455     if(!disableTrackOver){
31456         this.el.on("mouseover", this.onMouseOver, this);
31457         this.el.on("mouseout", this.onMouseOut, this);
31458     }
31459 };
31460
31461 // private
31462 Roo.Resizable.Handle.prototype = {
31463     afterResize : function(rz){
31464         Roo.log('after?');
31465         // do nothing
31466     },
31467     // private
31468     onMouseDown : function(e){
31469         this.rz.onMouseDown(this, e);
31470     },
31471     // private
31472     onMouseOver : function(e){
31473         this.rz.handleOver(this, e);
31474     },
31475     // private
31476     onMouseOut : function(e){
31477         this.rz.handleOut(this, e);
31478     }
31479 };/*
31480  * Based on:
31481  * Ext JS Library 1.1.1
31482  * Copyright(c) 2006-2007, Ext JS, LLC.
31483  *
31484  * Originally Released Under LGPL - original licence link has changed is not relivant.
31485  *
31486  * Fork - LGPL
31487  * <script type="text/javascript">
31488  */
31489
31490 /**
31491  * @class Roo.Editor
31492  * @extends Roo.Component
31493  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31494  * @constructor
31495  * Create a new Editor
31496  * @param {Roo.form.Field} field The Field object (or descendant)
31497  * @param {Object} config The config object
31498  */
31499 Roo.Editor = function(field, config){
31500     Roo.Editor.superclass.constructor.call(this, config);
31501     this.field = field;
31502     this.addEvents({
31503         /**
31504              * @event beforestartedit
31505              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31506              * false from the handler of this event.
31507              * @param {Editor} this
31508              * @param {Roo.Element} boundEl The underlying element bound to this editor
31509              * @param {Mixed} value The field value being set
31510              */
31511         "beforestartedit" : true,
31512         /**
31513              * @event startedit
31514              * Fires when this editor is displayed
31515              * @param {Roo.Element} boundEl The underlying element bound to this editor
31516              * @param {Mixed} value The starting field value
31517              */
31518         "startedit" : true,
31519         /**
31520              * @event beforecomplete
31521              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31522              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31523              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31524              * event will not fire since no edit actually occurred.
31525              * @param {Editor} this
31526              * @param {Mixed} value The current field value
31527              * @param {Mixed} startValue The original field value
31528              */
31529         "beforecomplete" : true,
31530         /**
31531              * @event complete
31532              * Fires after editing is complete and any changed value has been written to the underlying field.
31533              * @param {Editor} this
31534              * @param {Mixed} value The current field value
31535              * @param {Mixed} startValue The original field value
31536              */
31537         "complete" : true,
31538         /**
31539          * @event specialkey
31540          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31541          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31542          * @param {Roo.form.Field} this
31543          * @param {Roo.EventObject} e The event object
31544          */
31545         "specialkey" : true
31546     });
31547 };
31548
31549 Roo.extend(Roo.Editor, Roo.Component, {
31550     /**
31551      * @cfg {Boolean/String} autosize
31552      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31553      * or "height" to adopt the height only (defaults to false)
31554      */
31555     /**
31556      * @cfg {Boolean} revertInvalid
31557      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31558      * validation fails (defaults to true)
31559      */
31560     /**
31561      * @cfg {Boolean} ignoreNoChange
31562      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31563      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31564      * will never be ignored.
31565      */
31566     /**
31567      * @cfg {Boolean} hideEl
31568      * False to keep the bound element visible while the editor is displayed (defaults to true)
31569      */
31570     /**
31571      * @cfg {Mixed} value
31572      * The data value of the underlying field (defaults to "")
31573      */
31574     value : "",
31575     /**
31576      * @cfg {String} alignment
31577      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31578      */
31579     alignment: "c-c?",
31580     /**
31581      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31582      * for bottom-right shadow (defaults to "frame")
31583      */
31584     shadow : "frame",
31585     /**
31586      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31587      */
31588     constrain : false,
31589     /**
31590      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31591      */
31592     completeOnEnter : false,
31593     /**
31594      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31595      */
31596     cancelOnEsc : false,
31597     /**
31598      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31599      */
31600     updateEl : false,
31601
31602     // private
31603     onRender : function(ct, position){
31604         this.el = new Roo.Layer({
31605             shadow: this.shadow,
31606             cls: "x-editor",
31607             parentEl : ct,
31608             shim : this.shim,
31609             shadowOffset:4,
31610             id: this.id,
31611             constrain: this.constrain
31612         });
31613         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31614         if(this.field.msgTarget != 'title'){
31615             this.field.msgTarget = 'qtip';
31616         }
31617         this.field.render(this.el);
31618         if(Roo.isGecko){
31619             this.field.el.dom.setAttribute('autocomplete', 'off');
31620         }
31621         this.field.on("specialkey", this.onSpecialKey, this);
31622         if(this.swallowKeys){
31623             this.field.el.swallowEvent(['keydown','keypress']);
31624         }
31625         this.field.show();
31626         this.field.on("blur", this.onBlur, this);
31627         if(this.field.grow){
31628             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31629         }
31630     },
31631
31632     onSpecialKey : function(field, e)
31633     {
31634         //Roo.log('editor onSpecialKey');
31635         if(this.completeOnEnter && e.getKey() == e.ENTER){
31636             e.stopEvent();
31637             this.completeEdit();
31638             return;
31639         }
31640         // do not fire special key otherwise it might hide close the editor...
31641         if(e.getKey() == e.ENTER){    
31642             return;
31643         }
31644         if(this.cancelOnEsc && e.getKey() == e.ESC){
31645             this.cancelEdit();
31646             return;
31647         } 
31648         this.fireEvent('specialkey', field, e);
31649     
31650     },
31651
31652     /**
31653      * Starts the editing process and shows the editor.
31654      * @param {String/HTMLElement/Element} el The element to edit
31655      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31656       * to the innerHTML of el.
31657      */
31658     startEdit : function(el, value){
31659         if(this.editing){
31660             this.completeEdit();
31661         }
31662         this.boundEl = Roo.get(el);
31663         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31664         if(!this.rendered){
31665             this.render(this.parentEl || document.body);
31666         }
31667         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31668             return;
31669         }
31670         this.startValue = v;
31671         this.field.setValue(v);
31672         if(this.autoSize){
31673             var sz = this.boundEl.getSize();
31674             switch(this.autoSize){
31675                 case "width":
31676                 this.setSize(sz.width,  "");
31677                 break;
31678                 case "height":
31679                 this.setSize("",  sz.height);
31680                 break;
31681                 default:
31682                 this.setSize(sz.width,  sz.height);
31683             }
31684         }
31685         this.el.alignTo(this.boundEl, this.alignment);
31686         this.editing = true;
31687         if(Roo.QuickTips){
31688             Roo.QuickTips.disable();
31689         }
31690         this.show();
31691     },
31692
31693     /**
31694      * Sets the height and width of this editor.
31695      * @param {Number} width The new width
31696      * @param {Number} height The new height
31697      */
31698     setSize : function(w, h){
31699         this.field.setSize(w, h);
31700         if(this.el){
31701             this.el.sync();
31702         }
31703     },
31704
31705     /**
31706      * Realigns the editor to the bound field based on the current alignment config value.
31707      */
31708     realign : function(){
31709         this.el.alignTo(this.boundEl, this.alignment);
31710     },
31711
31712     /**
31713      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31714      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31715      */
31716     completeEdit : function(remainVisible){
31717         if(!this.editing){
31718             return;
31719         }
31720         var v = this.getValue();
31721         if(this.revertInvalid !== false && !this.field.isValid()){
31722             v = this.startValue;
31723             this.cancelEdit(true);
31724         }
31725         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31726             this.editing = false;
31727             this.hide();
31728             return;
31729         }
31730         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31731             this.editing = false;
31732             if(this.updateEl && this.boundEl){
31733                 this.boundEl.update(v);
31734             }
31735             if(remainVisible !== true){
31736                 this.hide();
31737             }
31738             this.fireEvent("complete", this, v, this.startValue);
31739         }
31740     },
31741
31742     // private
31743     onShow : function(){
31744         this.el.show();
31745         if(this.hideEl !== false){
31746             this.boundEl.hide();
31747         }
31748         this.field.show();
31749         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31750             this.fixIEFocus = true;
31751             this.deferredFocus.defer(50, this);
31752         }else{
31753             this.field.focus();
31754         }
31755         this.fireEvent("startedit", this.boundEl, this.startValue);
31756     },
31757
31758     deferredFocus : function(){
31759         if(this.editing){
31760             this.field.focus();
31761         }
31762     },
31763
31764     /**
31765      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31766      * reverted to the original starting value.
31767      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31768      * cancel (defaults to false)
31769      */
31770     cancelEdit : function(remainVisible){
31771         if(this.editing){
31772             this.setValue(this.startValue);
31773             if(remainVisible !== true){
31774                 this.hide();
31775             }
31776         }
31777     },
31778
31779     // private
31780     onBlur : function(){
31781         if(this.allowBlur !== true && this.editing){
31782             this.completeEdit();
31783         }
31784     },
31785
31786     // private
31787     onHide : function(){
31788         if(this.editing){
31789             this.completeEdit();
31790             return;
31791         }
31792         this.field.blur();
31793         if(this.field.collapse){
31794             this.field.collapse();
31795         }
31796         this.el.hide();
31797         if(this.hideEl !== false){
31798             this.boundEl.show();
31799         }
31800         if(Roo.QuickTips){
31801             Roo.QuickTips.enable();
31802         }
31803     },
31804
31805     /**
31806      * Sets the data value of the editor
31807      * @param {Mixed} value Any valid value supported by the underlying field
31808      */
31809     setValue : function(v){
31810         this.field.setValue(v);
31811     },
31812
31813     /**
31814      * Gets the data value of the editor
31815      * @return {Mixed} The data value
31816      */
31817     getValue : function(){
31818         return this.field.getValue();
31819     }
31820 });/*
31821  * Based on:
31822  * Ext JS Library 1.1.1
31823  * Copyright(c) 2006-2007, Ext JS, LLC.
31824  *
31825  * Originally Released Under LGPL - original licence link has changed is not relivant.
31826  *
31827  * Fork - LGPL
31828  * <script type="text/javascript">
31829  */
31830  
31831 /**
31832  * @class Roo.BasicDialog
31833  * @extends Roo.util.Observable
31834  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31835  * <pre><code>
31836 var dlg = new Roo.BasicDialog("my-dlg", {
31837     height: 200,
31838     width: 300,
31839     minHeight: 100,
31840     minWidth: 150,
31841     modal: true,
31842     proxyDrag: true,
31843     shadow: true
31844 });
31845 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31846 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31847 dlg.addButton('Cancel', dlg.hide, dlg);
31848 dlg.show();
31849 </code></pre>
31850   <b>A Dialog should always be a direct child of the body element.</b>
31851  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31852  * @cfg {String} title Default text to display in the title bar (defaults to null)
31853  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31854  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31855  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31856  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31857  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31858  * (defaults to null with no animation)
31859  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31860  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31861  * property for valid values (defaults to 'all')
31862  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31863  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31864  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31865  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31866  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31867  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31868  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31869  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31870  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31871  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31872  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31873  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31874  * draggable = true (defaults to false)
31875  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31876  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31877  * shadow (defaults to false)
31878  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31879  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31880  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31881  * @cfg {Array} buttons Array of buttons
31882  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31883  * @constructor
31884  * Create a new BasicDialog.
31885  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31886  * @param {Object} config Configuration options
31887  */
31888 Roo.BasicDialog = function(el, config){
31889     this.el = Roo.get(el);
31890     var dh = Roo.DomHelper;
31891     if(!this.el && config && config.autoCreate){
31892         if(typeof config.autoCreate == "object"){
31893             if(!config.autoCreate.id){
31894                 config.autoCreate.id = el;
31895             }
31896             this.el = dh.append(document.body,
31897                         config.autoCreate, true);
31898         }else{
31899             this.el = dh.append(document.body,
31900                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31901         }
31902     }
31903     el = this.el;
31904     el.setDisplayed(true);
31905     el.hide = this.hideAction;
31906     this.id = el.id;
31907     el.addClass("x-dlg");
31908
31909     Roo.apply(this, config);
31910
31911     this.proxy = el.createProxy("x-dlg-proxy");
31912     this.proxy.hide = this.hideAction;
31913     this.proxy.setOpacity(.5);
31914     this.proxy.hide();
31915
31916     if(config.width){
31917         el.setWidth(config.width);
31918     }
31919     if(config.height){
31920         el.setHeight(config.height);
31921     }
31922     this.size = el.getSize();
31923     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31924         this.xy = [config.x,config.y];
31925     }else{
31926         this.xy = el.getCenterXY(true);
31927     }
31928     /** The header element @type Roo.Element */
31929     this.header = el.child("> .x-dlg-hd");
31930     /** The body element @type Roo.Element */
31931     this.body = el.child("> .x-dlg-bd");
31932     /** The footer element @type Roo.Element */
31933     this.footer = el.child("> .x-dlg-ft");
31934
31935     if(!this.header){
31936         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31937     }
31938     if(!this.body){
31939         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31940     }
31941
31942     this.header.unselectable();
31943     if(this.title){
31944         this.header.update(this.title);
31945     }
31946     // this element allows the dialog to be focused for keyboard event
31947     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31948     this.focusEl.swallowEvent("click", true);
31949
31950     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31951
31952     // wrap the body and footer for special rendering
31953     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31954     if(this.footer){
31955         this.bwrap.dom.appendChild(this.footer.dom);
31956     }
31957
31958     this.bg = this.el.createChild({
31959         tag: "div", cls:"x-dlg-bg",
31960         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31961     });
31962     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31963
31964
31965     if(this.autoScroll !== false && !this.autoTabs){
31966         this.body.setStyle("overflow", "auto");
31967     }
31968
31969     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31970
31971     if(this.closable !== false){
31972         this.el.addClass("x-dlg-closable");
31973         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31974         this.close.on("click", this.closeClick, this);
31975         this.close.addClassOnOver("x-dlg-close-over");
31976     }
31977     if(this.collapsible !== false){
31978         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31979         this.collapseBtn.on("click", this.collapseClick, this);
31980         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31981         this.header.on("dblclick", this.collapseClick, this);
31982     }
31983     if(this.resizable !== false){
31984         this.el.addClass("x-dlg-resizable");
31985         this.resizer = new Roo.Resizable(el, {
31986             minWidth: this.minWidth || 80,
31987             minHeight:this.minHeight || 80,
31988             handles: this.resizeHandles || "all",
31989             pinned: true
31990         });
31991         this.resizer.on("beforeresize", this.beforeResize, this);
31992         this.resizer.on("resize", this.onResize, this);
31993     }
31994     if(this.draggable !== false){
31995         el.addClass("x-dlg-draggable");
31996         if (!this.proxyDrag) {
31997             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31998         }
31999         else {
32000             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32001         }
32002         dd.setHandleElId(this.header.id);
32003         dd.endDrag = this.endMove.createDelegate(this);
32004         dd.startDrag = this.startMove.createDelegate(this);
32005         dd.onDrag = this.onDrag.createDelegate(this);
32006         dd.scroll = false;
32007         this.dd = dd;
32008     }
32009     if(this.modal){
32010         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32011         this.mask.enableDisplayMode("block");
32012         this.mask.hide();
32013         this.el.addClass("x-dlg-modal");
32014     }
32015     if(this.shadow){
32016         this.shadow = new Roo.Shadow({
32017             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32018             offset : this.shadowOffset
32019         });
32020     }else{
32021         this.shadowOffset = 0;
32022     }
32023     if(Roo.useShims && this.shim !== false){
32024         this.shim = this.el.createShim();
32025         this.shim.hide = this.hideAction;
32026         this.shim.hide();
32027     }else{
32028         this.shim = false;
32029     }
32030     if(this.autoTabs){
32031         this.initTabs();
32032     }
32033     if (this.buttons) { 
32034         var bts= this.buttons;
32035         this.buttons = [];
32036         Roo.each(bts, function(b) {
32037             this.addButton(b);
32038         }, this);
32039     }
32040     
32041     
32042     this.addEvents({
32043         /**
32044          * @event keydown
32045          * Fires when a key is pressed
32046          * @param {Roo.BasicDialog} this
32047          * @param {Roo.EventObject} e
32048          */
32049         "keydown" : true,
32050         /**
32051          * @event move
32052          * Fires when this dialog is moved by the user.
32053          * @param {Roo.BasicDialog} this
32054          * @param {Number} x The new page X
32055          * @param {Number} y The new page Y
32056          */
32057         "move" : true,
32058         /**
32059          * @event resize
32060          * Fires when this dialog is resized by the user.
32061          * @param {Roo.BasicDialog} this
32062          * @param {Number} width The new width
32063          * @param {Number} height The new height
32064          */
32065         "resize" : true,
32066         /**
32067          * @event beforehide
32068          * Fires before this dialog is hidden.
32069          * @param {Roo.BasicDialog} this
32070          */
32071         "beforehide" : true,
32072         /**
32073          * @event hide
32074          * Fires when this dialog is hidden.
32075          * @param {Roo.BasicDialog} this
32076          */
32077         "hide" : true,
32078         /**
32079          * @event beforeshow
32080          * Fires before this dialog is shown.
32081          * @param {Roo.BasicDialog} this
32082          */
32083         "beforeshow" : true,
32084         /**
32085          * @event show
32086          * Fires when this dialog is shown.
32087          * @param {Roo.BasicDialog} this
32088          */
32089         "show" : true
32090     });
32091     el.on("keydown", this.onKeyDown, this);
32092     el.on("mousedown", this.toFront, this);
32093     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32094     this.el.hide();
32095     Roo.DialogManager.register(this);
32096     Roo.BasicDialog.superclass.constructor.call(this);
32097 };
32098
32099 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32100     shadowOffset: Roo.isIE ? 6 : 5,
32101     minHeight: 80,
32102     minWidth: 200,
32103     minButtonWidth: 75,
32104     defaultButton: null,
32105     buttonAlign: "right",
32106     tabTag: 'div',
32107     firstShow: true,
32108
32109     /**
32110      * Sets the dialog title text
32111      * @param {String} text The title text to display
32112      * @return {Roo.BasicDialog} this
32113      */
32114     setTitle : function(text){
32115         this.header.update(text);
32116         return this;
32117     },
32118
32119     // private
32120     closeClick : function(){
32121         this.hide();
32122     },
32123
32124     // private
32125     collapseClick : function(){
32126         this[this.collapsed ? "expand" : "collapse"]();
32127     },
32128
32129     /**
32130      * Collapses the dialog to its minimized state (only the title bar is visible).
32131      * Equivalent to the user clicking the collapse dialog button.
32132      */
32133     collapse : function(){
32134         if(!this.collapsed){
32135             this.collapsed = true;
32136             this.el.addClass("x-dlg-collapsed");
32137             this.restoreHeight = this.el.getHeight();
32138             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32139         }
32140     },
32141
32142     /**
32143      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32144      * clicking the expand dialog button.
32145      */
32146     expand : function(){
32147         if(this.collapsed){
32148             this.collapsed = false;
32149             this.el.removeClass("x-dlg-collapsed");
32150             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32151         }
32152     },
32153
32154     /**
32155      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32156      * @return {Roo.TabPanel} The tabs component
32157      */
32158     initTabs : function(){
32159         var tabs = this.getTabs();
32160         while(tabs.getTab(0)){
32161             tabs.removeTab(0);
32162         }
32163         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32164             var dom = el.dom;
32165             tabs.addTab(Roo.id(dom), dom.title);
32166             dom.title = "";
32167         });
32168         tabs.activate(0);
32169         return tabs;
32170     },
32171
32172     // private
32173     beforeResize : function(){
32174         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32175     },
32176
32177     // private
32178     onResize : function(){
32179         this.refreshSize();
32180         this.syncBodyHeight();
32181         this.adjustAssets();
32182         this.focus();
32183         this.fireEvent("resize", this, this.size.width, this.size.height);
32184     },
32185
32186     // private
32187     onKeyDown : function(e){
32188         if(this.isVisible()){
32189             this.fireEvent("keydown", this, e);
32190         }
32191     },
32192
32193     /**
32194      * Resizes the dialog.
32195      * @param {Number} width
32196      * @param {Number} height
32197      * @return {Roo.BasicDialog} this
32198      */
32199     resizeTo : function(width, height){
32200         this.el.setSize(width, height);
32201         this.size = {width: width, height: height};
32202         this.syncBodyHeight();
32203         if(this.fixedcenter){
32204             this.center();
32205         }
32206         if(this.isVisible()){
32207             this.constrainXY();
32208             this.adjustAssets();
32209         }
32210         this.fireEvent("resize", this, width, height);
32211         return this;
32212     },
32213
32214
32215     /**
32216      * Resizes the dialog to fit the specified content size.
32217      * @param {Number} width
32218      * @param {Number} height
32219      * @return {Roo.BasicDialog} this
32220      */
32221     setContentSize : function(w, h){
32222         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32223         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32224         //if(!this.el.isBorderBox()){
32225             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32226             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32227         //}
32228         if(this.tabs){
32229             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32230             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32231         }
32232         this.resizeTo(w, h);
32233         return this;
32234     },
32235
32236     /**
32237      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32238      * executed in response to a particular key being pressed while the dialog is active.
32239      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32240      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32241      * @param {Function} fn The function to call
32242      * @param {Object} scope (optional) The scope of the function
32243      * @return {Roo.BasicDialog} this
32244      */
32245     addKeyListener : function(key, fn, scope){
32246         var keyCode, shift, ctrl, alt;
32247         if(typeof key == "object" && !(key instanceof Array)){
32248             keyCode = key["key"];
32249             shift = key["shift"];
32250             ctrl = key["ctrl"];
32251             alt = key["alt"];
32252         }else{
32253             keyCode = key;
32254         }
32255         var handler = function(dlg, e){
32256             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32257                 var k = e.getKey();
32258                 if(keyCode instanceof Array){
32259                     for(var i = 0, len = keyCode.length; i < len; i++){
32260                         if(keyCode[i] == k){
32261                           fn.call(scope || window, dlg, k, e);
32262                           return;
32263                         }
32264                     }
32265                 }else{
32266                     if(k == keyCode){
32267                         fn.call(scope || window, dlg, k, e);
32268                     }
32269                 }
32270             }
32271         };
32272         this.on("keydown", handler);
32273         return this;
32274     },
32275
32276     /**
32277      * Returns the TabPanel component (creates it if it doesn't exist).
32278      * Note: If you wish to simply check for the existence of tabs without creating them,
32279      * check for a null 'tabs' property.
32280      * @return {Roo.TabPanel} The tabs component
32281      */
32282     getTabs : function(){
32283         if(!this.tabs){
32284             this.el.addClass("x-dlg-auto-tabs");
32285             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32286             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32287         }
32288         return this.tabs;
32289     },
32290
32291     /**
32292      * Adds a button to the footer section of the dialog.
32293      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32294      * object or a valid Roo.DomHelper element config
32295      * @param {Function} handler The function called when the button is clicked
32296      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32297      * @return {Roo.Button} The new button
32298      */
32299     addButton : function(config, handler, scope){
32300         var dh = Roo.DomHelper;
32301         if(!this.footer){
32302             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32303         }
32304         if(!this.btnContainer){
32305             var tb = this.footer.createChild({
32306
32307                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32308                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32309             }, null, true);
32310             this.btnContainer = tb.firstChild.firstChild.firstChild;
32311         }
32312         var bconfig = {
32313             handler: handler,
32314             scope: scope,
32315             minWidth: this.minButtonWidth,
32316             hideParent:true
32317         };
32318         if(typeof config == "string"){
32319             bconfig.text = config;
32320         }else{
32321             if(config.tag){
32322                 bconfig.dhconfig = config;
32323             }else{
32324                 Roo.apply(bconfig, config);
32325             }
32326         }
32327         var fc = false;
32328         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32329             bconfig.position = Math.max(0, bconfig.position);
32330             fc = this.btnContainer.childNodes[bconfig.position];
32331         }
32332          
32333         var btn = new Roo.Button(
32334             fc ? 
32335                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32336                 : this.btnContainer.appendChild(document.createElement("td")),
32337             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32338             bconfig
32339         );
32340         this.syncBodyHeight();
32341         if(!this.buttons){
32342             /**
32343              * Array of all the buttons that have been added to this dialog via addButton
32344              * @type Array
32345              */
32346             this.buttons = [];
32347         }
32348         this.buttons.push(btn);
32349         return btn;
32350     },
32351
32352     /**
32353      * Sets the default button to be focused when the dialog is displayed.
32354      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32355      * @return {Roo.BasicDialog} this
32356      */
32357     setDefaultButton : function(btn){
32358         this.defaultButton = btn;
32359         return this;
32360     },
32361
32362     // private
32363     getHeaderFooterHeight : function(safe){
32364         var height = 0;
32365         if(this.header){
32366            height += this.header.getHeight();
32367         }
32368         if(this.footer){
32369            var fm = this.footer.getMargins();
32370             height += (this.footer.getHeight()+fm.top+fm.bottom);
32371         }
32372         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32373         height += this.centerBg.getPadding("tb");
32374         return height;
32375     },
32376
32377     // private
32378     syncBodyHeight : function()
32379     {
32380         var bd = this.body, // the text
32381             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32382             bw = this.bwrap;
32383         var height = this.size.height - this.getHeaderFooterHeight(false);
32384         bd.setHeight(height-bd.getMargins("tb"));
32385         var hh = this.header.getHeight();
32386         var h = this.size.height-hh;
32387         cb.setHeight(h);
32388         
32389         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32390         bw.setHeight(h-cb.getPadding("tb"));
32391         
32392         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32393         bd.setWidth(bw.getWidth(true));
32394         if(this.tabs){
32395             this.tabs.syncHeight();
32396             if(Roo.isIE){
32397                 this.tabs.el.repaint();
32398             }
32399         }
32400     },
32401
32402     /**
32403      * Restores the previous state of the dialog if Roo.state is configured.
32404      * @return {Roo.BasicDialog} this
32405      */
32406     restoreState : function(){
32407         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32408         if(box && box.width){
32409             this.xy = [box.x, box.y];
32410             this.resizeTo(box.width, box.height);
32411         }
32412         return this;
32413     },
32414
32415     // private
32416     beforeShow : function(){
32417         this.expand();
32418         if(this.fixedcenter){
32419             this.xy = this.el.getCenterXY(true);
32420         }
32421         if(this.modal){
32422             Roo.get(document.body).addClass("x-body-masked");
32423             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32424             this.mask.show();
32425         }
32426         this.constrainXY();
32427     },
32428
32429     // private
32430     animShow : function(){
32431         var b = Roo.get(this.animateTarget).getBox();
32432         this.proxy.setSize(b.width, b.height);
32433         this.proxy.setLocation(b.x, b.y);
32434         this.proxy.show();
32435         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32436                     true, .35, this.showEl.createDelegate(this));
32437     },
32438
32439     /**
32440      * Shows the dialog.
32441      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32442      * @return {Roo.BasicDialog} this
32443      */
32444     show : function(animateTarget){
32445         if (this.fireEvent("beforeshow", this) === false){
32446             return;
32447         }
32448         if(this.syncHeightBeforeShow){
32449             this.syncBodyHeight();
32450         }else if(this.firstShow){
32451             this.firstShow = false;
32452             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32453         }
32454         this.animateTarget = animateTarget || this.animateTarget;
32455         if(!this.el.isVisible()){
32456             this.beforeShow();
32457             if(this.animateTarget && Roo.get(this.animateTarget)){
32458                 this.animShow();
32459             }else{
32460                 this.showEl();
32461             }
32462         }
32463         return this;
32464     },
32465
32466     // private
32467     showEl : function(){
32468         this.proxy.hide();
32469         this.el.setXY(this.xy);
32470         this.el.show();
32471         this.adjustAssets(true);
32472         this.toFront();
32473         this.focus();
32474         // IE peekaboo bug - fix found by Dave Fenwick
32475         if(Roo.isIE){
32476             this.el.repaint();
32477         }
32478         this.fireEvent("show", this);
32479     },
32480
32481     /**
32482      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32483      * dialog itself will receive focus.
32484      */
32485     focus : function(){
32486         if(this.defaultButton){
32487             this.defaultButton.focus();
32488         }else{
32489             this.focusEl.focus();
32490         }
32491     },
32492
32493     // private
32494     constrainXY : function(){
32495         if(this.constraintoviewport !== false){
32496             if(!this.viewSize){
32497                 if(this.container){
32498                     var s = this.container.getSize();
32499                     this.viewSize = [s.width, s.height];
32500                 }else{
32501                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32502                 }
32503             }
32504             var s = Roo.get(this.container||document).getScroll();
32505
32506             var x = this.xy[0], y = this.xy[1];
32507             var w = this.size.width, h = this.size.height;
32508             var vw = this.viewSize[0], vh = this.viewSize[1];
32509             // only move it if it needs it
32510             var moved = false;
32511             // first validate right/bottom
32512             if(x + w > vw+s.left){
32513                 x = vw - w;
32514                 moved = true;
32515             }
32516             if(y + h > vh+s.top){
32517                 y = vh - h;
32518                 moved = true;
32519             }
32520             // then make sure top/left isn't negative
32521             if(x < s.left){
32522                 x = s.left;
32523                 moved = true;
32524             }
32525             if(y < s.top){
32526                 y = s.top;
32527                 moved = true;
32528             }
32529             if(moved){
32530                 // cache xy
32531                 this.xy = [x, y];
32532                 if(this.isVisible()){
32533                     this.el.setLocation(x, y);
32534                     this.adjustAssets();
32535                 }
32536             }
32537         }
32538     },
32539
32540     // private
32541     onDrag : function(){
32542         if(!this.proxyDrag){
32543             this.xy = this.el.getXY();
32544             this.adjustAssets();
32545         }
32546     },
32547
32548     // private
32549     adjustAssets : function(doShow){
32550         var x = this.xy[0], y = this.xy[1];
32551         var w = this.size.width, h = this.size.height;
32552         if(doShow === true){
32553             if(this.shadow){
32554                 this.shadow.show(this.el);
32555             }
32556             if(this.shim){
32557                 this.shim.show();
32558             }
32559         }
32560         if(this.shadow && this.shadow.isVisible()){
32561             this.shadow.show(this.el);
32562         }
32563         if(this.shim && this.shim.isVisible()){
32564             this.shim.setBounds(x, y, w, h);
32565         }
32566     },
32567
32568     // private
32569     adjustViewport : function(w, h){
32570         if(!w || !h){
32571             w = Roo.lib.Dom.getViewWidth();
32572             h = Roo.lib.Dom.getViewHeight();
32573         }
32574         // cache the size
32575         this.viewSize = [w, h];
32576         if(this.modal && this.mask.isVisible()){
32577             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32578             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32579         }
32580         if(this.isVisible()){
32581             this.constrainXY();
32582         }
32583     },
32584
32585     /**
32586      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32587      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32588      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32589      */
32590     destroy : function(removeEl){
32591         if(this.isVisible()){
32592             this.animateTarget = null;
32593             this.hide();
32594         }
32595         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32596         if(this.tabs){
32597             this.tabs.destroy(removeEl);
32598         }
32599         Roo.destroy(
32600              this.shim,
32601              this.proxy,
32602              this.resizer,
32603              this.close,
32604              this.mask
32605         );
32606         if(this.dd){
32607             this.dd.unreg();
32608         }
32609         if(this.buttons){
32610            for(var i = 0, len = this.buttons.length; i < len; i++){
32611                this.buttons[i].destroy();
32612            }
32613         }
32614         this.el.removeAllListeners();
32615         if(removeEl === true){
32616             this.el.update("");
32617             this.el.remove();
32618         }
32619         Roo.DialogManager.unregister(this);
32620     },
32621
32622     // private
32623     startMove : function(){
32624         if(this.proxyDrag){
32625             this.proxy.show();
32626         }
32627         if(this.constraintoviewport !== false){
32628             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32629         }
32630     },
32631
32632     // private
32633     endMove : function(){
32634         if(!this.proxyDrag){
32635             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32636         }else{
32637             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32638             this.proxy.hide();
32639         }
32640         this.refreshSize();
32641         this.adjustAssets();
32642         this.focus();
32643         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32644     },
32645
32646     /**
32647      * Brings this dialog to the front of any other visible dialogs
32648      * @return {Roo.BasicDialog} this
32649      */
32650     toFront : function(){
32651         Roo.DialogManager.bringToFront(this);
32652         return this;
32653     },
32654
32655     /**
32656      * Sends this dialog to the back (under) of any other visible dialogs
32657      * @return {Roo.BasicDialog} this
32658      */
32659     toBack : function(){
32660         Roo.DialogManager.sendToBack(this);
32661         return this;
32662     },
32663
32664     /**
32665      * Centers this dialog in the viewport
32666      * @return {Roo.BasicDialog} this
32667      */
32668     center : function(){
32669         var xy = this.el.getCenterXY(true);
32670         this.moveTo(xy[0], xy[1]);
32671         return this;
32672     },
32673
32674     /**
32675      * Moves the dialog's top-left corner to the specified point
32676      * @param {Number} x
32677      * @param {Number} y
32678      * @return {Roo.BasicDialog} this
32679      */
32680     moveTo : function(x, y){
32681         this.xy = [x,y];
32682         if(this.isVisible()){
32683             this.el.setXY(this.xy);
32684             this.adjustAssets();
32685         }
32686         return this;
32687     },
32688
32689     /**
32690      * Aligns the dialog to the specified element
32691      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32692      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32693      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32694      * @return {Roo.BasicDialog} this
32695      */
32696     alignTo : function(element, position, offsets){
32697         this.xy = this.el.getAlignToXY(element, position, offsets);
32698         if(this.isVisible()){
32699             this.el.setXY(this.xy);
32700             this.adjustAssets();
32701         }
32702         return this;
32703     },
32704
32705     /**
32706      * Anchors an element to another element and realigns it when the window is resized.
32707      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32708      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32709      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32710      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32711      * is a number, it is used as the buffer delay (defaults to 50ms).
32712      * @return {Roo.BasicDialog} this
32713      */
32714     anchorTo : function(el, alignment, offsets, monitorScroll){
32715         var action = function(){
32716             this.alignTo(el, alignment, offsets);
32717         };
32718         Roo.EventManager.onWindowResize(action, this);
32719         var tm = typeof monitorScroll;
32720         if(tm != 'undefined'){
32721             Roo.EventManager.on(window, 'scroll', action, this,
32722                 {buffer: tm == 'number' ? monitorScroll : 50});
32723         }
32724         action.call(this);
32725         return this;
32726     },
32727
32728     /**
32729      * Returns true if the dialog is visible
32730      * @return {Boolean}
32731      */
32732     isVisible : function(){
32733         return this.el.isVisible();
32734     },
32735
32736     // private
32737     animHide : function(callback){
32738         var b = Roo.get(this.animateTarget).getBox();
32739         this.proxy.show();
32740         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32741         this.el.hide();
32742         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32743                     this.hideEl.createDelegate(this, [callback]));
32744     },
32745
32746     /**
32747      * Hides the dialog.
32748      * @param {Function} callback (optional) Function to call when the dialog is hidden
32749      * @return {Roo.BasicDialog} this
32750      */
32751     hide : function(callback){
32752         if (this.fireEvent("beforehide", this) === false){
32753             return;
32754         }
32755         if(this.shadow){
32756             this.shadow.hide();
32757         }
32758         if(this.shim) {
32759           this.shim.hide();
32760         }
32761         // sometimes animateTarget seems to get set.. causing problems...
32762         // this just double checks..
32763         if(this.animateTarget && Roo.get(this.animateTarget)) {
32764            this.animHide(callback);
32765         }else{
32766             this.el.hide();
32767             this.hideEl(callback);
32768         }
32769         return this;
32770     },
32771
32772     // private
32773     hideEl : function(callback){
32774         this.proxy.hide();
32775         if(this.modal){
32776             this.mask.hide();
32777             Roo.get(document.body).removeClass("x-body-masked");
32778         }
32779         this.fireEvent("hide", this);
32780         if(typeof callback == "function"){
32781             callback();
32782         }
32783     },
32784
32785     // private
32786     hideAction : function(){
32787         this.setLeft("-10000px");
32788         this.setTop("-10000px");
32789         this.setStyle("visibility", "hidden");
32790     },
32791
32792     // private
32793     refreshSize : function(){
32794         this.size = this.el.getSize();
32795         this.xy = this.el.getXY();
32796         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32797     },
32798
32799     // private
32800     // z-index is managed by the DialogManager and may be overwritten at any time
32801     setZIndex : function(index){
32802         if(this.modal){
32803             this.mask.setStyle("z-index", index);
32804         }
32805         if(this.shim){
32806             this.shim.setStyle("z-index", ++index);
32807         }
32808         if(this.shadow){
32809             this.shadow.setZIndex(++index);
32810         }
32811         this.el.setStyle("z-index", ++index);
32812         if(this.proxy){
32813             this.proxy.setStyle("z-index", ++index);
32814         }
32815         if(this.resizer){
32816             this.resizer.proxy.setStyle("z-index", ++index);
32817         }
32818
32819         this.lastZIndex = index;
32820     },
32821
32822     /**
32823      * Returns the element for this dialog
32824      * @return {Roo.Element} The underlying dialog Element
32825      */
32826     getEl : function(){
32827         return this.el;
32828     }
32829 });
32830
32831 /**
32832  * @class Roo.DialogManager
32833  * Provides global access to BasicDialogs that have been created and
32834  * support for z-indexing (layering) multiple open dialogs.
32835  */
32836 Roo.DialogManager = function(){
32837     var list = {};
32838     var accessList = [];
32839     var front = null;
32840
32841     // private
32842     var sortDialogs = function(d1, d2){
32843         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32844     };
32845
32846     // private
32847     var orderDialogs = function(){
32848         accessList.sort(sortDialogs);
32849         var seed = Roo.DialogManager.zseed;
32850         for(var i = 0, len = accessList.length; i < len; i++){
32851             var dlg = accessList[i];
32852             if(dlg){
32853                 dlg.setZIndex(seed + (i*10));
32854             }
32855         }
32856     };
32857
32858     return {
32859         /**
32860          * The starting z-index for BasicDialogs (defaults to 9000)
32861          * @type Number The z-index value
32862          */
32863         zseed : 9000,
32864
32865         // private
32866         register : function(dlg){
32867             list[dlg.id] = dlg;
32868             accessList.push(dlg);
32869         },
32870
32871         // private
32872         unregister : function(dlg){
32873             delete list[dlg.id];
32874             var i=0;
32875             var len=0;
32876             if(!accessList.indexOf){
32877                 for(  i = 0, len = accessList.length; i < len; i++){
32878                     if(accessList[i] == dlg){
32879                         accessList.splice(i, 1);
32880                         return;
32881                     }
32882                 }
32883             }else{
32884                  i = accessList.indexOf(dlg);
32885                 if(i != -1){
32886                     accessList.splice(i, 1);
32887                 }
32888             }
32889         },
32890
32891         /**
32892          * Gets a registered dialog by id
32893          * @param {String/Object} id The id of the dialog or a dialog
32894          * @return {Roo.BasicDialog} this
32895          */
32896         get : function(id){
32897             return typeof id == "object" ? id : list[id];
32898         },
32899
32900         /**
32901          * Brings the specified dialog to the front
32902          * @param {String/Object} dlg The id of the dialog or a dialog
32903          * @return {Roo.BasicDialog} this
32904          */
32905         bringToFront : function(dlg){
32906             dlg = this.get(dlg);
32907             if(dlg != front){
32908                 front = dlg;
32909                 dlg._lastAccess = new Date().getTime();
32910                 orderDialogs();
32911             }
32912             return dlg;
32913         },
32914
32915         /**
32916          * Sends the specified dialog to the back
32917          * @param {String/Object} dlg The id of the dialog or a dialog
32918          * @return {Roo.BasicDialog} this
32919          */
32920         sendToBack : function(dlg){
32921             dlg = this.get(dlg);
32922             dlg._lastAccess = -(new Date().getTime());
32923             orderDialogs();
32924             return dlg;
32925         },
32926
32927         /**
32928          * Hides all dialogs
32929          */
32930         hideAll : function(){
32931             for(var id in list){
32932                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32933                     list[id].hide();
32934                 }
32935             }
32936         }
32937     };
32938 }();
32939
32940 /**
32941  * @class Roo.LayoutDialog
32942  * @extends Roo.BasicDialog
32943  * Dialog which provides adjustments for working with a layout in a Dialog.
32944  * Add your necessary layout config options to the dialog's config.<br>
32945  * Example usage (including a nested layout):
32946  * <pre><code>
32947 if(!dialog){
32948     dialog = new Roo.LayoutDialog("download-dlg", {
32949         modal: true,
32950         width:600,
32951         height:450,
32952         shadow:true,
32953         minWidth:500,
32954         minHeight:350,
32955         autoTabs:true,
32956         proxyDrag:true,
32957         // layout config merges with the dialog config
32958         center:{
32959             tabPosition: "top",
32960             alwaysShowTabs: true
32961         }
32962     });
32963     dialog.addKeyListener(27, dialog.hide, dialog);
32964     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32965     dialog.addButton("Build It!", this.getDownload, this);
32966
32967     // we can even add nested layouts
32968     var innerLayout = new Roo.BorderLayout("dl-inner", {
32969         east: {
32970             initialSize: 200,
32971             autoScroll:true,
32972             split:true
32973         },
32974         center: {
32975             autoScroll:true
32976         }
32977     });
32978     innerLayout.beginUpdate();
32979     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32980     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32981     innerLayout.endUpdate(true);
32982
32983     var layout = dialog.getLayout();
32984     layout.beginUpdate();
32985     layout.add("center", new Roo.ContentPanel("standard-panel",
32986                         {title: "Download the Source", fitToFrame:true}));
32987     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32988                {title: "Build your own roo.js"}));
32989     layout.getRegion("center").showPanel(sp);
32990     layout.endUpdate();
32991 }
32992 </code></pre>
32993     * @constructor
32994     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32995     * @param {Object} config configuration options
32996   */
32997 Roo.LayoutDialog = function(el, cfg){
32998     
32999     var config=  cfg;
33000     if (typeof(cfg) == 'undefined') {
33001         config = Roo.apply({}, el);
33002         // not sure why we use documentElement here.. - it should always be body.
33003         // IE7 borks horribly if we use documentElement.
33004         // webkit also does not like documentElement - it creates a body element...
33005         el = Roo.get( document.body || document.documentElement ).createChild();
33006         //config.autoCreate = true;
33007     }
33008     
33009     
33010     config.autoTabs = false;
33011     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33012     this.body.setStyle({overflow:"hidden", position:"relative"});
33013     this.layout = new Roo.BorderLayout(this.body.dom, config);
33014     this.layout.monitorWindowResize = false;
33015     this.el.addClass("x-dlg-auto-layout");
33016     // fix case when center region overwrites center function
33017     this.center = Roo.BasicDialog.prototype.center;
33018     this.on("show", this.layout.layout, this.layout, true);
33019     if (config.items) {
33020         var xitems = config.items;
33021         delete config.items;
33022         Roo.each(xitems, this.addxtype, this);
33023     }
33024     
33025     
33026 };
33027 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33028     /**
33029      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33030      * @deprecated
33031      */
33032     endUpdate : function(){
33033         this.layout.endUpdate();
33034     },
33035
33036     /**
33037      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33038      *  @deprecated
33039      */
33040     beginUpdate : function(){
33041         this.layout.beginUpdate();
33042     },
33043
33044     /**
33045      * Get the BorderLayout for this dialog
33046      * @return {Roo.BorderLayout}
33047      */
33048     getLayout : function(){
33049         return this.layout;
33050     },
33051
33052     showEl : function(){
33053         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33054         if(Roo.isIE7){
33055             this.layout.layout();
33056         }
33057     },
33058
33059     // private
33060     // Use the syncHeightBeforeShow config option to control this automatically
33061     syncBodyHeight : function(){
33062         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33063         if(this.layout){this.layout.layout();}
33064     },
33065     
33066       /**
33067      * Add an xtype element (actually adds to the layout.)
33068      * @return {Object} xdata xtype object data.
33069      */
33070     
33071     addxtype : function(c) {
33072         return this.layout.addxtype(c);
33073     }
33074 });/*
33075  * Based on:
33076  * Ext JS Library 1.1.1
33077  * Copyright(c) 2006-2007, Ext JS, LLC.
33078  *
33079  * Originally Released Under LGPL - original licence link has changed is not relivant.
33080  *
33081  * Fork - LGPL
33082  * <script type="text/javascript">
33083  */
33084  
33085 /**
33086  * @class Roo.MessageBox
33087  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33088  * Example usage:
33089  *<pre><code>
33090 // Basic alert:
33091 Roo.Msg.alert('Status', 'Changes saved successfully.');
33092
33093 // Prompt for user data:
33094 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33095     if (btn == 'ok'){
33096         // process text value...
33097     }
33098 });
33099
33100 // Show a dialog using config options:
33101 Roo.Msg.show({
33102    title:'Save Changes?',
33103    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33104    buttons: Roo.Msg.YESNOCANCEL,
33105    fn: processResult,
33106    animEl: 'elId'
33107 });
33108 </code></pre>
33109  * @singleton
33110  */
33111 Roo.MessageBox = function(){
33112     var dlg, opt, mask, waitTimer;
33113     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33114     var buttons, activeTextEl, bwidth;
33115
33116     // private
33117     var handleButton = function(button){
33118         dlg.hide();
33119         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33120     };
33121
33122     // private
33123     var handleHide = function(){
33124         if(opt && opt.cls){
33125             dlg.el.removeClass(opt.cls);
33126         }
33127         if(waitTimer){
33128             Roo.TaskMgr.stop(waitTimer);
33129             waitTimer = null;
33130         }
33131     };
33132
33133     // private
33134     var updateButtons = function(b){
33135         var width = 0;
33136         if(!b){
33137             buttons["ok"].hide();
33138             buttons["cancel"].hide();
33139             buttons["yes"].hide();
33140             buttons["no"].hide();
33141             dlg.footer.dom.style.display = 'none';
33142             return width;
33143         }
33144         dlg.footer.dom.style.display = '';
33145         for(var k in buttons){
33146             if(typeof buttons[k] != "function"){
33147                 if(b[k]){
33148                     buttons[k].show();
33149                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33150                     width += buttons[k].el.getWidth()+15;
33151                 }else{
33152                     buttons[k].hide();
33153                 }
33154             }
33155         }
33156         return width;
33157     };
33158
33159     // private
33160     var handleEsc = function(d, k, e){
33161         if(opt && opt.closable !== false){
33162             dlg.hide();
33163         }
33164         if(e){
33165             e.stopEvent();
33166         }
33167     };
33168
33169     return {
33170         /**
33171          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33172          * @return {Roo.BasicDialog} The BasicDialog element
33173          */
33174         getDialog : function(){
33175            if(!dlg){
33176                 dlg = new Roo.BasicDialog("x-msg-box", {
33177                     autoCreate : true,
33178                     shadow: true,
33179                     draggable: true,
33180                     resizable:false,
33181                     constraintoviewport:false,
33182                     fixedcenter:true,
33183                     collapsible : false,
33184                     shim:true,
33185                     modal: true,
33186                     width:400, height:100,
33187                     buttonAlign:"center",
33188                     closeClick : function(){
33189                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33190                             handleButton("no");
33191                         }else{
33192                             handleButton("cancel");
33193                         }
33194                     }
33195                 });
33196                 dlg.on("hide", handleHide);
33197                 mask = dlg.mask;
33198                 dlg.addKeyListener(27, handleEsc);
33199                 buttons = {};
33200                 var bt = this.buttonText;
33201                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33202                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33203                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33204                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33205                 bodyEl = dlg.body.createChild({
33206
33207                     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>'
33208                 });
33209                 msgEl = bodyEl.dom.firstChild;
33210                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33211                 textboxEl.enableDisplayMode();
33212                 textboxEl.addKeyListener([10,13], function(){
33213                     if(dlg.isVisible() && opt && opt.buttons){
33214                         if(opt.buttons.ok){
33215                             handleButton("ok");
33216                         }else if(opt.buttons.yes){
33217                             handleButton("yes");
33218                         }
33219                     }
33220                 });
33221                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33222                 textareaEl.enableDisplayMode();
33223                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33224                 progressEl.enableDisplayMode();
33225                 var pf = progressEl.dom.firstChild;
33226                 if (pf) {
33227                     pp = Roo.get(pf.firstChild);
33228                     pp.setHeight(pf.offsetHeight);
33229                 }
33230                 
33231             }
33232             return dlg;
33233         },
33234
33235         /**
33236          * Updates the message box body text
33237          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33238          * the XHTML-compliant non-breaking space character '&amp;#160;')
33239          * @return {Roo.MessageBox} This message box
33240          */
33241         updateText : function(text){
33242             if(!dlg.isVisible() && !opt.width){
33243                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33244             }
33245             msgEl.innerHTML = text || '&#160;';
33246       
33247             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33248             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33249             var w = Math.max(
33250                     Math.min(opt.width || cw , this.maxWidth), 
33251                     Math.max(opt.minWidth || this.minWidth, bwidth)
33252             );
33253             if(opt.prompt){
33254                 activeTextEl.setWidth(w);
33255             }
33256             if(dlg.isVisible()){
33257                 dlg.fixedcenter = false;
33258             }
33259             // to big, make it scroll. = But as usual stupid IE does not support
33260             // !important..
33261             
33262             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33263                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33264                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33265             } else {
33266                 bodyEl.dom.style.height = '';
33267                 bodyEl.dom.style.overflowY = '';
33268             }
33269             if (cw > w) {
33270                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33271             } else {
33272                 bodyEl.dom.style.overflowX = '';
33273             }
33274             
33275             dlg.setContentSize(w, bodyEl.getHeight());
33276             if(dlg.isVisible()){
33277                 dlg.fixedcenter = true;
33278             }
33279             return this;
33280         },
33281
33282         /**
33283          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33284          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33285          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33286          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33287          * @return {Roo.MessageBox} This message box
33288          */
33289         updateProgress : function(value, text){
33290             if(text){
33291                 this.updateText(text);
33292             }
33293             if (pp) { // weird bug on my firefox - for some reason this is not defined
33294                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33295             }
33296             return this;
33297         },        
33298
33299         /**
33300          * Returns true if the message box is currently displayed
33301          * @return {Boolean} True if the message box is visible, else false
33302          */
33303         isVisible : function(){
33304             return dlg && dlg.isVisible();  
33305         },
33306
33307         /**
33308          * Hides the message box if it is displayed
33309          */
33310         hide : function(){
33311             if(this.isVisible()){
33312                 dlg.hide();
33313             }  
33314         },
33315
33316         /**
33317          * Displays a new message box, or reinitializes an existing message box, based on the config options
33318          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33319          * The following config object properties are supported:
33320          * <pre>
33321 Property    Type             Description
33322 ----------  ---------------  ------------------------------------------------------------------------------------
33323 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33324                                    closes (defaults to undefined)
33325 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33326                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33327 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33328                                    progress and wait dialogs will ignore this property and always hide the
33329                                    close button as they can only be closed programmatically.
33330 cls               String           A custom CSS class to apply to the message box element
33331 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33332                                    displayed (defaults to 75)
33333 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33334                                    function will be btn (the name of the button that was clicked, if applicable,
33335                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33336                                    Progress and wait dialogs will ignore this option since they do not respond to
33337                                    user actions and can only be closed programmatically, so any required function
33338                                    should be called by the same code after it closes the dialog.
33339 icon              String           A CSS class that provides a background image to be used as an icon for
33340                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33341 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33342 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33343 modal             Boolean          False to allow user interaction with the page while the message box is
33344                                    displayed (defaults to true)
33345 msg               String           A string that will replace the existing message box body text (defaults
33346                                    to the XHTML-compliant non-breaking space character '&#160;')
33347 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33348 progress          Boolean          True to display a progress bar (defaults to false)
33349 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33350 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33351 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33352 title             String           The title text
33353 value             String           The string value to set into the active textbox element if displayed
33354 wait              Boolean          True to display a progress bar (defaults to false)
33355 width             Number           The width of the dialog in pixels
33356 </pre>
33357          *
33358          * Example usage:
33359          * <pre><code>
33360 Roo.Msg.show({
33361    title: 'Address',
33362    msg: 'Please enter your address:',
33363    width: 300,
33364    buttons: Roo.MessageBox.OKCANCEL,
33365    multiline: true,
33366    fn: saveAddress,
33367    animEl: 'addAddressBtn'
33368 });
33369 </code></pre>
33370          * @param {Object} config Configuration options
33371          * @return {Roo.MessageBox} This message box
33372          */
33373         show : function(options)
33374         {
33375             
33376             // this causes nightmares if you show one dialog after another
33377             // especially on callbacks..
33378              
33379             if(this.isVisible()){
33380                 
33381                 this.hide();
33382                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33383                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33384                 Roo.log("New Dialog Message:" +  options.msg )
33385                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33386                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33387                 
33388             }
33389             var d = this.getDialog();
33390             opt = options;
33391             d.setTitle(opt.title || "&#160;");
33392             d.close.setDisplayed(opt.closable !== false);
33393             activeTextEl = textboxEl;
33394             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33395             if(opt.prompt){
33396                 if(opt.multiline){
33397                     textboxEl.hide();
33398                     textareaEl.show();
33399                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33400                         opt.multiline : this.defaultTextHeight);
33401                     activeTextEl = textareaEl;
33402                 }else{
33403                     textboxEl.show();
33404                     textareaEl.hide();
33405                 }
33406             }else{
33407                 textboxEl.hide();
33408                 textareaEl.hide();
33409             }
33410             progressEl.setDisplayed(opt.progress === true);
33411             this.updateProgress(0);
33412             activeTextEl.dom.value = opt.value || "";
33413             if(opt.prompt){
33414                 dlg.setDefaultButton(activeTextEl);
33415             }else{
33416                 var bs = opt.buttons;
33417                 var db = null;
33418                 if(bs && bs.ok){
33419                     db = buttons["ok"];
33420                 }else if(bs && bs.yes){
33421                     db = buttons["yes"];
33422                 }
33423                 dlg.setDefaultButton(db);
33424             }
33425             bwidth = updateButtons(opt.buttons);
33426             this.updateText(opt.msg);
33427             if(opt.cls){
33428                 d.el.addClass(opt.cls);
33429             }
33430             d.proxyDrag = opt.proxyDrag === true;
33431             d.modal = opt.modal !== false;
33432             d.mask = opt.modal !== false ? mask : false;
33433             if(!d.isVisible()){
33434                 // force it to the end of the z-index stack so it gets a cursor in FF
33435                 document.body.appendChild(dlg.el.dom);
33436                 d.animateTarget = null;
33437                 d.show(options.animEl);
33438             }
33439             return this;
33440         },
33441
33442         /**
33443          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33444          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33445          * and closing the message box when the process is complete.
33446          * @param {String} title The title bar text
33447          * @param {String} msg The message box body text
33448          * @return {Roo.MessageBox} This message box
33449          */
33450         progress : function(title, msg){
33451             this.show({
33452                 title : title,
33453                 msg : msg,
33454                 buttons: false,
33455                 progress:true,
33456                 closable:false,
33457                 minWidth: this.minProgressWidth,
33458                 modal : true
33459             });
33460             return this;
33461         },
33462
33463         /**
33464          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33465          * If a callback function is passed it will be called after the user clicks the button, and the
33466          * id of the button that was clicked will be passed as the only parameter to the callback
33467          * (could also be the top-right close button).
33468          * @param {String} title The title bar text
33469          * @param {String} msg The message box body text
33470          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33471          * @param {Object} scope (optional) The scope of the callback function
33472          * @return {Roo.MessageBox} This message box
33473          */
33474         alert : function(title, msg, fn, scope){
33475             this.show({
33476                 title : title,
33477                 msg : msg,
33478                 buttons: this.OK,
33479                 fn: fn,
33480                 scope : scope,
33481                 modal : true
33482             });
33483             return this;
33484         },
33485
33486         /**
33487          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33488          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33489          * You are responsible for closing the message box when the process is complete.
33490          * @param {String} msg The message box body text
33491          * @param {String} title (optional) The title bar text
33492          * @return {Roo.MessageBox} This message box
33493          */
33494         wait : function(msg, title){
33495             this.show({
33496                 title : title,
33497                 msg : msg,
33498                 buttons: false,
33499                 closable:false,
33500                 progress:true,
33501                 modal:true,
33502                 width:300,
33503                 wait:true
33504             });
33505             waitTimer = Roo.TaskMgr.start({
33506                 run: function(i){
33507                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33508                 },
33509                 interval: 1000
33510             });
33511             return this;
33512         },
33513
33514         /**
33515          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33516          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33517          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33518          * @param {String} title The title bar text
33519          * @param {String} msg The message box body text
33520          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33521          * @param {Object} scope (optional) The scope of the callback function
33522          * @return {Roo.MessageBox} This message box
33523          */
33524         confirm : function(title, msg, fn, scope){
33525             this.show({
33526                 title : title,
33527                 msg : msg,
33528                 buttons: this.YESNO,
33529                 fn: fn,
33530                 scope : scope,
33531                 modal : true
33532             });
33533             return this;
33534         },
33535
33536         /**
33537          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33538          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33539          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33540          * (could also be the top-right close button) and the text that was entered will be passed as the two
33541          * parameters to the callback.
33542          * @param {String} title The title bar text
33543          * @param {String} msg The message box body text
33544          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33545          * @param {Object} scope (optional) The scope of the callback function
33546          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33547          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33548          * @return {Roo.MessageBox} This message box
33549          */
33550         prompt : function(title, msg, fn, scope, multiline){
33551             this.show({
33552                 title : title,
33553                 msg : msg,
33554                 buttons: this.OKCANCEL,
33555                 fn: fn,
33556                 minWidth:250,
33557                 scope : scope,
33558                 prompt:true,
33559                 multiline: multiline,
33560                 modal : true
33561             });
33562             return this;
33563         },
33564
33565         /**
33566          * Button config that displays a single OK button
33567          * @type Object
33568          */
33569         OK : {ok:true},
33570         /**
33571          * Button config that displays Yes and No buttons
33572          * @type Object
33573          */
33574         YESNO : {yes:true, no:true},
33575         /**
33576          * Button config that displays OK and Cancel buttons
33577          * @type Object
33578          */
33579         OKCANCEL : {ok:true, cancel:true},
33580         /**
33581          * Button config that displays Yes, No and Cancel buttons
33582          * @type Object
33583          */
33584         YESNOCANCEL : {yes:true, no:true, cancel:true},
33585
33586         /**
33587          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33588          * @type Number
33589          */
33590         defaultTextHeight : 75,
33591         /**
33592          * The maximum width in pixels of the message box (defaults to 600)
33593          * @type Number
33594          */
33595         maxWidth : 600,
33596         /**
33597          * The minimum width in pixels of the message box (defaults to 100)
33598          * @type Number
33599          */
33600         minWidth : 100,
33601         /**
33602          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33603          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33604          * @type Number
33605          */
33606         minProgressWidth : 250,
33607         /**
33608          * An object containing the default button text strings that can be overriden for localized language support.
33609          * Supported properties are: ok, cancel, yes and no.
33610          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33611          * @type Object
33612          */
33613         buttonText : {
33614             ok : "OK",
33615             cancel : "Cancel",
33616             yes : "Yes",
33617             no : "No"
33618         }
33619     };
33620 }();
33621
33622 /**
33623  * Shorthand for {@link Roo.MessageBox}
33624  */
33625 Roo.Msg = Roo.MessageBox;/*
33626  * Based on:
33627  * Ext JS Library 1.1.1
33628  * Copyright(c) 2006-2007, Ext JS, LLC.
33629  *
33630  * Originally Released Under LGPL - original licence link has changed is not relivant.
33631  *
33632  * Fork - LGPL
33633  * <script type="text/javascript">
33634  */
33635 /**
33636  * @class Roo.QuickTips
33637  * Provides attractive and customizable tooltips for any element.
33638  * @singleton
33639  */
33640 Roo.QuickTips = function(){
33641     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33642     var ce, bd, xy, dd;
33643     var visible = false, disabled = true, inited = false;
33644     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33645     
33646     var onOver = function(e){
33647         if(disabled){
33648             return;
33649         }
33650         var t = e.getTarget();
33651         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33652             return;
33653         }
33654         if(ce && t == ce.el){
33655             clearTimeout(hideProc);
33656             return;
33657         }
33658         if(t && tagEls[t.id]){
33659             tagEls[t.id].el = t;
33660             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33661             return;
33662         }
33663         var ttp, et = Roo.fly(t);
33664         var ns = cfg.namespace;
33665         if(tm.interceptTitles && t.title){
33666             ttp = t.title;
33667             t.qtip = ttp;
33668             t.removeAttribute("title");
33669             e.preventDefault();
33670         }else{
33671             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33672         }
33673         if(ttp){
33674             showProc = show.defer(tm.showDelay, tm, [{
33675                 el: t, 
33676                 text: ttp.replace(/\\n/g,'<br/>'),
33677                 width: et.getAttributeNS(ns, cfg.width),
33678                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33679                 title: et.getAttributeNS(ns, cfg.title),
33680                     cls: et.getAttributeNS(ns, cfg.cls)
33681             }]);
33682         }
33683     };
33684     
33685     var onOut = function(e){
33686         clearTimeout(showProc);
33687         var t = e.getTarget();
33688         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33689             hideProc = setTimeout(hide, tm.hideDelay);
33690         }
33691     };
33692     
33693     var onMove = function(e){
33694         if(disabled){
33695             return;
33696         }
33697         xy = e.getXY();
33698         xy[1] += 18;
33699         if(tm.trackMouse && ce){
33700             el.setXY(xy);
33701         }
33702     };
33703     
33704     var onDown = function(e){
33705         clearTimeout(showProc);
33706         clearTimeout(hideProc);
33707         if(!e.within(el)){
33708             if(tm.hideOnClick){
33709                 hide();
33710                 tm.disable();
33711                 tm.enable.defer(100, tm);
33712             }
33713         }
33714     };
33715     
33716     var getPad = function(){
33717         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33718     };
33719
33720     var show = function(o){
33721         if(disabled){
33722             return;
33723         }
33724         clearTimeout(dismissProc);
33725         ce = o;
33726         if(removeCls){ // in case manually hidden
33727             el.removeClass(removeCls);
33728             removeCls = null;
33729         }
33730         if(ce.cls){
33731             el.addClass(ce.cls);
33732             removeCls = ce.cls;
33733         }
33734         if(ce.title){
33735             tipTitle.update(ce.title);
33736             tipTitle.show();
33737         }else{
33738             tipTitle.update('');
33739             tipTitle.hide();
33740         }
33741         el.dom.style.width  = tm.maxWidth+'px';
33742         //tipBody.dom.style.width = '';
33743         tipBodyText.update(o.text);
33744         var p = getPad(), w = ce.width;
33745         if(!w){
33746             var td = tipBodyText.dom;
33747             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33748             if(aw > tm.maxWidth){
33749                 w = tm.maxWidth;
33750             }else if(aw < tm.minWidth){
33751                 w = tm.minWidth;
33752             }else{
33753                 w = aw;
33754             }
33755         }
33756         //tipBody.setWidth(w);
33757         el.setWidth(parseInt(w, 10) + p);
33758         if(ce.autoHide === false){
33759             close.setDisplayed(true);
33760             if(dd){
33761                 dd.unlock();
33762             }
33763         }else{
33764             close.setDisplayed(false);
33765             if(dd){
33766                 dd.lock();
33767             }
33768         }
33769         if(xy){
33770             el.avoidY = xy[1]-18;
33771             el.setXY(xy);
33772         }
33773         if(tm.animate){
33774             el.setOpacity(.1);
33775             el.setStyle("visibility", "visible");
33776             el.fadeIn({callback: afterShow});
33777         }else{
33778             afterShow();
33779         }
33780     };
33781     
33782     var afterShow = function(){
33783         if(ce){
33784             el.show();
33785             esc.enable();
33786             if(tm.autoDismiss && ce.autoHide !== false){
33787                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33788             }
33789         }
33790     };
33791     
33792     var hide = function(noanim){
33793         clearTimeout(dismissProc);
33794         clearTimeout(hideProc);
33795         ce = null;
33796         if(el.isVisible()){
33797             esc.disable();
33798             if(noanim !== true && tm.animate){
33799                 el.fadeOut({callback: afterHide});
33800             }else{
33801                 afterHide();
33802             } 
33803         }
33804     };
33805     
33806     var afterHide = function(){
33807         el.hide();
33808         if(removeCls){
33809             el.removeClass(removeCls);
33810             removeCls = null;
33811         }
33812     };
33813     
33814     return {
33815         /**
33816         * @cfg {Number} minWidth
33817         * The minimum width of the quick tip (defaults to 40)
33818         */
33819        minWidth : 40,
33820         /**
33821         * @cfg {Number} maxWidth
33822         * The maximum width of the quick tip (defaults to 300)
33823         */
33824        maxWidth : 300,
33825         /**
33826         * @cfg {Boolean} interceptTitles
33827         * True to automatically use the element's DOM title value if available (defaults to false)
33828         */
33829        interceptTitles : false,
33830         /**
33831         * @cfg {Boolean} trackMouse
33832         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33833         */
33834        trackMouse : false,
33835         /**
33836         * @cfg {Boolean} hideOnClick
33837         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33838         */
33839        hideOnClick : true,
33840         /**
33841         * @cfg {Number} showDelay
33842         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33843         */
33844        showDelay : 500,
33845         /**
33846         * @cfg {Number} hideDelay
33847         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33848         */
33849        hideDelay : 200,
33850         /**
33851         * @cfg {Boolean} autoHide
33852         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33853         * Used in conjunction with hideDelay.
33854         */
33855        autoHide : true,
33856         /**
33857         * @cfg {Boolean}
33858         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33859         * (defaults to true).  Used in conjunction with autoDismissDelay.
33860         */
33861        autoDismiss : true,
33862         /**
33863         * @cfg {Number}
33864         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33865         */
33866        autoDismissDelay : 5000,
33867        /**
33868         * @cfg {Boolean} animate
33869         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33870         */
33871        animate : false,
33872
33873        /**
33874         * @cfg {String} title
33875         * Title text to display (defaults to '').  This can be any valid HTML markup.
33876         */
33877         title: '',
33878        /**
33879         * @cfg {String} text
33880         * Body text to display (defaults to '').  This can be any valid HTML markup.
33881         */
33882         text : '',
33883        /**
33884         * @cfg {String} cls
33885         * A CSS class to apply to the base quick tip element (defaults to '').
33886         */
33887         cls : '',
33888        /**
33889         * @cfg {Number} width
33890         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33891         * minWidth or maxWidth.
33892         */
33893         width : null,
33894
33895     /**
33896      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33897      * or display QuickTips in a page.
33898      */
33899        init : function(){
33900           tm = Roo.QuickTips;
33901           cfg = tm.tagConfig;
33902           if(!inited){
33903               if(!Roo.isReady){ // allow calling of init() before onReady
33904                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33905                   return;
33906               }
33907               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33908               el.fxDefaults = {stopFx: true};
33909               // maximum custom styling
33910               //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>');
33911               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>');              
33912               tipTitle = el.child('h3');
33913               tipTitle.enableDisplayMode("block");
33914               tipBody = el.child('div.x-tip-bd');
33915               tipBodyText = el.child('div.x-tip-bd-inner');
33916               //bdLeft = el.child('div.x-tip-bd-left');
33917               //bdRight = el.child('div.x-tip-bd-right');
33918               close = el.child('div.x-tip-close');
33919               close.enableDisplayMode("block");
33920               close.on("click", hide);
33921               var d = Roo.get(document);
33922               d.on("mousedown", onDown);
33923               d.on("mouseover", onOver);
33924               d.on("mouseout", onOut);
33925               d.on("mousemove", onMove);
33926               esc = d.addKeyListener(27, hide);
33927               esc.disable();
33928               if(Roo.dd.DD){
33929                   dd = el.initDD("default", null, {
33930                       onDrag : function(){
33931                           el.sync();  
33932                       }
33933                   });
33934                   dd.setHandleElId(tipTitle.id);
33935                   dd.lock();
33936               }
33937               inited = true;
33938           }
33939           this.enable(); 
33940        },
33941
33942     /**
33943      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33944      * are supported:
33945      * <pre>
33946 Property    Type                   Description
33947 ----------  ---------------------  ------------------------------------------------------------------------
33948 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33949      * </ul>
33950      * @param {Object} config The config object
33951      */
33952        register : function(config){
33953            var cs = config instanceof Array ? config : arguments;
33954            for(var i = 0, len = cs.length; i < len; i++) {
33955                var c = cs[i];
33956                var target = c.target;
33957                if(target){
33958                    if(target instanceof Array){
33959                        for(var j = 0, jlen = target.length; j < jlen; j++){
33960                            tagEls[target[j]] = c;
33961                        }
33962                    }else{
33963                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33964                    }
33965                }
33966            }
33967        },
33968
33969     /**
33970      * Removes this quick tip from its element and destroys it.
33971      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33972      */
33973        unregister : function(el){
33974            delete tagEls[Roo.id(el)];
33975        },
33976
33977     /**
33978      * Enable this quick tip.
33979      */
33980        enable : function(){
33981            if(inited && disabled){
33982                locks.pop();
33983                if(locks.length < 1){
33984                    disabled = false;
33985                }
33986            }
33987        },
33988
33989     /**
33990      * Disable this quick tip.
33991      */
33992        disable : function(){
33993           disabled = true;
33994           clearTimeout(showProc);
33995           clearTimeout(hideProc);
33996           clearTimeout(dismissProc);
33997           if(ce){
33998               hide(true);
33999           }
34000           locks.push(1);
34001        },
34002
34003     /**
34004      * Returns true if the quick tip is enabled, else false.
34005      */
34006        isEnabled : function(){
34007             return !disabled;
34008        },
34009
34010         // private
34011        tagConfig : {
34012            namespace : "roo", // was ext?? this may break..
34013            alt_namespace : "ext",
34014            attribute : "qtip",
34015            width : "width",
34016            target : "target",
34017            title : "qtitle",
34018            hide : "hide",
34019            cls : "qclass"
34020        }
34021    };
34022 }();
34023
34024 // backwards compat
34025 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34026  * Based on:
34027  * Ext JS Library 1.1.1
34028  * Copyright(c) 2006-2007, Ext JS, LLC.
34029  *
34030  * Originally Released Under LGPL - original licence link has changed is not relivant.
34031  *
34032  * Fork - LGPL
34033  * <script type="text/javascript">
34034  */
34035  
34036
34037 /**
34038  * @class Roo.tree.TreePanel
34039  * @extends Roo.data.Tree
34040
34041  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34042  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34043  * @cfg {Boolean} enableDD true to enable drag and drop
34044  * @cfg {Boolean} enableDrag true to enable just drag
34045  * @cfg {Boolean} enableDrop true to enable just drop
34046  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34047  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34048  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34049  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34050  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34051  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34052  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34053  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34054  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34055  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34056  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34057  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34058  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34059  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34060  * @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>
34061  * @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>
34062  * 
34063  * @constructor
34064  * @param {String/HTMLElement/Element} el The container element
34065  * @param {Object} config
34066  */
34067 Roo.tree.TreePanel = function(el, config){
34068     var root = false;
34069     var loader = false;
34070     if (config.root) {
34071         root = config.root;
34072         delete config.root;
34073     }
34074     if (config.loader) {
34075         loader = config.loader;
34076         delete config.loader;
34077     }
34078     
34079     Roo.apply(this, config);
34080     Roo.tree.TreePanel.superclass.constructor.call(this);
34081     this.el = Roo.get(el);
34082     this.el.addClass('x-tree');
34083     //console.log(root);
34084     if (root) {
34085         this.setRootNode( Roo.factory(root, Roo.tree));
34086     }
34087     if (loader) {
34088         this.loader = Roo.factory(loader, Roo.tree);
34089     }
34090    /**
34091     * Read-only. The id of the container element becomes this TreePanel's id.
34092     */
34093     this.id = this.el.id;
34094     this.addEvents({
34095         /**
34096         * @event beforeload
34097         * Fires before a node is loaded, return false to cancel
34098         * @param {Node} node The node being loaded
34099         */
34100         "beforeload" : true,
34101         /**
34102         * @event load
34103         * Fires when a node is loaded
34104         * @param {Node} node The node that was loaded
34105         */
34106         "load" : true,
34107         /**
34108         * @event textchange
34109         * Fires when the text for a node is changed
34110         * @param {Node} node The node
34111         * @param {String} text The new text
34112         * @param {String} oldText The old text
34113         */
34114         "textchange" : true,
34115         /**
34116         * @event beforeexpand
34117         * Fires before a node is expanded, return false to cancel.
34118         * @param {Node} node The node
34119         * @param {Boolean} deep
34120         * @param {Boolean} anim
34121         */
34122         "beforeexpand" : true,
34123         /**
34124         * @event beforecollapse
34125         * Fires before a node is collapsed, return false to cancel.
34126         * @param {Node} node The node
34127         * @param {Boolean} deep
34128         * @param {Boolean} anim
34129         */
34130         "beforecollapse" : true,
34131         /**
34132         * @event expand
34133         * Fires when a node is expanded
34134         * @param {Node} node The node
34135         */
34136         "expand" : true,
34137         /**
34138         * @event disabledchange
34139         * Fires when the disabled status of a node changes
34140         * @param {Node} node The node
34141         * @param {Boolean} disabled
34142         */
34143         "disabledchange" : true,
34144         /**
34145         * @event collapse
34146         * Fires when a node is collapsed
34147         * @param {Node} node The node
34148         */
34149         "collapse" : true,
34150         /**
34151         * @event beforeclick
34152         * Fires before click processing on a node. Return false to cancel the default action.
34153         * @param {Node} node The node
34154         * @param {Roo.EventObject} e The event object
34155         */
34156         "beforeclick":true,
34157         /**
34158         * @event checkchange
34159         * Fires when a node with a checkbox's checked property changes
34160         * @param {Node} this This node
34161         * @param {Boolean} checked
34162         */
34163         "checkchange":true,
34164         /**
34165         * @event click
34166         * Fires when a node is clicked
34167         * @param {Node} node The node
34168         * @param {Roo.EventObject} e The event object
34169         */
34170         "click":true,
34171         /**
34172         * @event dblclick
34173         * Fires when a node is double clicked
34174         * @param {Node} node The node
34175         * @param {Roo.EventObject} e The event object
34176         */
34177         "dblclick":true,
34178         /**
34179         * @event contextmenu
34180         * Fires when a node is right clicked
34181         * @param {Node} node The node
34182         * @param {Roo.EventObject} e The event object
34183         */
34184         "contextmenu":true,
34185         /**
34186         * @event beforechildrenrendered
34187         * Fires right before the child nodes for a node are rendered
34188         * @param {Node} node The node
34189         */
34190         "beforechildrenrendered":true,
34191         /**
34192         * @event startdrag
34193         * Fires when a node starts being dragged
34194         * @param {Roo.tree.TreePanel} this
34195         * @param {Roo.tree.TreeNode} node
34196         * @param {event} e The raw browser event
34197         */ 
34198        "startdrag" : true,
34199        /**
34200         * @event enddrag
34201         * Fires when a drag operation is complete
34202         * @param {Roo.tree.TreePanel} this
34203         * @param {Roo.tree.TreeNode} node
34204         * @param {event} e The raw browser event
34205         */
34206        "enddrag" : true,
34207        /**
34208         * @event dragdrop
34209         * Fires when a dragged node is dropped on a valid DD target
34210         * @param {Roo.tree.TreePanel} this
34211         * @param {Roo.tree.TreeNode} node
34212         * @param {DD} dd The dd it was dropped on
34213         * @param {event} e The raw browser event
34214         */
34215        "dragdrop" : true,
34216        /**
34217         * @event beforenodedrop
34218         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34219         * passed to handlers has the following properties:<br />
34220         * <ul style="padding:5px;padding-left:16px;">
34221         * <li>tree - The TreePanel</li>
34222         * <li>target - The node being targeted for the drop</li>
34223         * <li>data - The drag data from the drag source</li>
34224         * <li>point - The point of the drop - append, above or below</li>
34225         * <li>source - The drag source</li>
34226         * <li>rawEvent - Raw mouse event</li>
34227         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34228         * to be inserted by setting them on this object.</li>
34229         * <li>cancel - Set this to true to cancel the drop.</li>
34230         * </ul>
34231         * @param {Object} dropEvent
34232         */
34233        "beforenodedrop" : true,
34234        /**
34235         * @event nodedrop
34236         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34237         * passed to handlers has the following properties:<br />
34238         * <ul style="padding:5px;padding-left:16px;">
34239         * <li>tree - The TreePanel</li>
34240         * <li>target - The node being targeted for the drop</li>
34241         * <li>data - The drag data from the drag source</li>
34242         * <li>point - The point of the drop - append, above or below</li>
34243         * <li>source - The drag source</li>
34244         * <li>rawEvent - Raw mouse event</li>
34245         * <li>dropNode - Dropped node(s).</li>
34246         * </ul>
34247         * @param {Object} dropEvent
34248         */
34249        "nodedrop" : true,
34250         /**
34251         * @event nodedragover
34252         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34253         * passed to handlers has the following properties:<br />
34254         * <ul style="padding:5px;padding-left:16px;">
34255         * <li>tree - The TreePanel</li>
34256         * <li>target - The node being targeted for the drop</li>
34257         * <li>data - The drag data from the drag source</li>
34258         * <li>point - The point of the drop - append, above or below</li>
34259         * <li>source - The drag source</li>
34260         * <li>rawEvent - Raw mouse event</li>
34261         * <li>dropNode - Drop node(s) provided by the source.</li>
34262         * <li>cancel - Set this to true to signal drop not allowed.</li>
34263         * </ul>
34264         * @param {Object} dragOverEvent
34265         */
34266        "nodedragover" : true
34267         
34268     });
34269     if(this.singleExpand){
34270        this.on("beforeexpand", this.restrictExpand, this);
34271     }
34272     if (this.editor) {
34273         this.editor.tree = this;
34274         this.editor = Roo.factory(this.editor, Roo.tree);
34275     }
34276     
34277     if (this.selModel) {
34278         this.selModel = Roo.factory(this.selModel, Roo.tree);
34279     }
34280    
34281 };
34282 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34283     rootVisible : true,
34284     animate: Roo.enableFx,
34285     lines : true,
34286     enableDD : false,
34287     hlDrop : Roo.enableFx,
34288   
34289     renderer: false,
34290     
34291     rendererTip: false,
34292     // private
34293     restrictExpand : function(node){
34294         var p = node.parentNode;
34295         if(p){
34296             if(p.expandedChild && p.expandedChild.parentNode == p){
34297                 p.expandedChild.collapse();
34298             }
34299             p.expandedChild = node;
34300         }
34301     },
34302
34303     // private override
34304     setRootNode : function(node){
34305         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34306         if(!this.rootVisible){
34307             node.ui = new Roo.tree.RootTreeNodeUI(node);
34308         }
34309         return node;
34310     },
34311
34312     /**
34313      * Returns the container element for this TreePanel
34314      */
34315     getEl : function(){
34316         return this.el;
34317     },
34318
34319     /**
34320      * Returns the default TreeLoader for this TreePanel
34321      */
34322     getLoader : function(){
34323         return this.loader;
34324     },
34325
34326     /**
34327      * Expand all nodes
34328      */
34329     expandAll : function(){
34330         this.root.expand(true);
34331     },
34332
34333     /**
34334      * Collapse all nodes
34335      */
34336     collapseAll : function(){
34337         this.root.collapse(true);
34338     },
34339
34340     /**
34341      * Returns the selection model used by this TreePanel
34342      */
34343     getSelectionModel : function(){
34344         if(!this.selModel){
34345             this.selModel = new Roo.tree.DefaultSelectionModel();
34346         }
34347         return this.selModel;
34348     },
34349
34350     /**
34351      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34352      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34353      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34354      * @return {Array}
34355      */
34356     getChecked : function(a, startNode){
34357         startNode = startNode || this.root;
34358         var r = [];
34359         var f = function(){
34360             if(this.attributes.checked){
34361                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34362             }
34363         }
34364         startNode.cascade(f);
34365         return r;
34366     },
34367
34368     /**
34369      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34370      * @param {String} path
34371      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34372      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34373      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34374      */
34375     expandPath : function(path, attr, callback){
34376         attr = attr || "id";
34377         var keys = path.split(this.pathSeparator);
34378         var curNode = this.root;
34379         if(curNode.attributes[attr] != keys[1]){ // invalid root
34380             if(callback){
34381                 callback(false, null);
34382             }
34383             return;
34384         }
34385         var index = 1;
34386         var f = function(){
34387             if(++index == keys.length){
34388                 if(callback){
34389                     callback(true, curNode);
34390                 }
34391                 return;
34392             }
34393             var c = curNode.findChild(attr, keys[index]);
34394             if(!c){
34395                 if(callback){
34396                     callback(false, curNode);
34397                 }
34398                 return;
34399             }
34400             curNode = c;
34401             c.expand(false, false, f);
34402         };
34403         curNode.expand(false, false, f);
34404     },
34405
34406     /**
34407      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34408      * @param {String} path
34409      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34410      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34411      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34412      */
34413     selectPath : function(path, attr, callback){
34414         attr = attr || "id";
34415         var keys = path.split(this.pathSeparator);
34416         var v = keys.pop();
34417         if(keys.length > 0){
34418             var f = function(success, node){
34419                 if(success && node){
34420                     var n = node.findChild(attr, v);
34421                     if(n){
34422                         n.select();
34423                         if(callback){
34424                             callback(true, n);
34425                         }
34426                     }else if(callback){
34427                         callback(false, n);
34428                     }
34429                 }else{
34430                     if(callback){
34431                         callback(false, n);
34432                     }
34433                 }
34434             };
34435             this.expandPath(keys.join(this.pathSeparator), attr, f);
34436         }else{
34437             this.root.select();
34438             if(callback){
34439                 callback(true, this.root);
34440             }
34441         }
34442     },
34443
34444     getTreeEl : function(){
34445         return this.el;
34446     },
34447
34448     /**
34449      * Trigger rendering of this TreePanel
34450      */
34451     render : function(){
34452         if (this.innerCt) {
34453             return this; // stop it rendering more than once!!
34454         }
34455         
34456         this.innerCt = this.el.createChild({tag:"ul",
34457                cls:"x-tree-root-ct " +
34458                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34459
34460         if(this.containerScroll){
34461             Roo.dd.ScrollManager.register(this.el);
34462         }
34463         if((this.enableDD || this.enableDrop) && !this.dropZone){
34464            /**
34465             * The dropZone used by this tree if drop is enabled
34466             * @type Roo.tree.TreeDropZone
34467             */
34468              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34469                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34470            });
34471         }
34472         if((this.enableDD || this.enableDrag) && !this.dragZone){
34473            /**
34474             * The dragZone used by this tree if drag is enabled
34475             * @type Roo.tree.TreeDragZone
34476             */
34477             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34478                ddGroup: this.ddGroup || "TreeDD",
34479                scroll: this.ddScroll
34480            });
34481         }
34482         this.getSelectionModel().init(this);
34483         if (!this.root) {
34484             Roo.log("ROOT not set in tree");
34485             return this;
34486         }
34487         this.root.render();
34488         if(!this.rootVisible){
34489             this.root.renderChildren();
34490         }
34491         return this;
34492     }
34493 });/*
34494  * Based on:
34495  * Ext JS Library 1.1.1
34496  * Copyright(c) 2006-2007, Ext JS, LLC.
34497  *
34498  * Originally Released Under LGPL - original licence link has changed is not relivant.
34499  *
34500  * Fork - LGPL
34501  * <script type="text/javascript">
34502  */
34503  
34504
34505 /**
34506  * @class Roo.tree.DefaultSelectionModel
34507  * @extends Roo.util.Observable
34508  * The default single selection for a TreePanel.
34509  * @param {Object} cfg Configuration
34510  */
34511 Roo.tree.DefaultSelectionModel = function(cfg){
34512    this.selNode = null;
34513    
34514    
34515    
34516    this.addEvents({
34517        /**
34518         * @event selectionchange
34519         * Fires when the selected node changes
34520         * @param {DefaultSelectionModel} this
34521         * @param {TreeNode} node the new selection
34522         */
34523        "selectionchange" : true,
34524
34525        /**
34526         * @event beforeselect
34527         * Fires before the selected node changes, return false to cancel the change
34528         * @param {DefaultSelectionModel} this
34529         * @param {TreeNode} node the new selection
34530         * @param {TreeNode} node the old selection
34531         */
34532        "beforeselect" : true
34533    });
34534    
34535     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34536 };
34537
34538 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34539     init : function(tree){
34540         this.tree = tree;
34541         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34542         tree.on("click", this.onNodeClick, this);
34543     },
34544     
34545     onNodeClick : function(node, e){
34546         if (e.ctrlKey && this.selNode == node)  {
34547             this.unselect(node);
34548             return;
34549         }
34550         this.select(node);
34551     },
34552     
34553     /**
34554      * Select a node.
34555      * @param {TreeNode} node The node to select
34556      * @return {TreeNode} The selected node
34557      */
34558     select : function(node){
34559         var last = this.selNode;
34560         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34561             if(last){
34562                 last.ui.onSelectedChange(false);
34563             }
34564             this.selNode = node;
34565             node.ui.onSelectedChange(true);
34566             this.fireEvent("selectionchange", this, node, last);
34567         }
34568         return node;
34569     },
34570     
34571     /**
34572      * Deselect a node.
34573      * @param {TreeNode} node The node to unselect
34574      */
34575     unselect : function(node){
34576         if(this.selNode == node){
34577             this.clearSelections();
34578         }    
34579     },
34580     
34581     /**
34582      * Clear all selections
34583      */
34584     clearSelections : function(){
34585         var n = this.selNode;
34586         if(n){
34587             n.ui.onSelectedChange(false);
34588             this.selNode = null;
34589             this.fireEvent("selectionchange", this, null);
34590         }
34591         return n;
34592     },
34593     
34594     /**
34595      * Get the selected node
34596      * @return {TreeNode} The selected node
34597      */
34598     getSelectedNode : function(){
34599         return this.selNode;    
34600     },
34601     
34602     /**
34603      * Returns true if the node is selected
34604      * @param {TreeNode} node The node to check
34605      * @return {Boolean}
34606      */
34607     isSelected : function(node){
34608         return this.selNode == node;  
34609     },
34610
34611     /**
34612      * Selects the node above the selected node in the tree, intelligently walking the nodes
34613      * @return TreeNode The new selection
34614      */
34615     selectPrevious : function(){
34616         var s = this.selNode || this.lastSelNode;
34617         if(!s){
34618             return null;
34619         }
34620         var ps = s.previousSibling;
34621         if(ps){
34622             if(!ps.isExpanded() || ps.childNodes.length < 1){
34623                 return this.select(ps);
34624             } else{
34625                 var lc = ps.lastChild;
34626                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34627                     lc = lc.lastChild;
34628                 }
34629                 return this.select(lc);
34630             }
34631         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34632             return this.select(s.parentNode);
34633         }
34634         return null;
34635     },
34636
34637     /**
34638      * Selects the node above the selected node in the tree, intelligently walking the nodes
34639      * @return TreeNode The new selection
34640      */
34641     selectNext : function(){
34642         var s = this.selNode || this.lastSelNode;
34643         if(!s){
34644             return null;
34645         }
34646         if(s.firstChild && s.isExpanded()){
34647              return this.select(s.firstChild);
34648          }else if(s.nextSibling){
34649              return this.select(s.nextSibling);
34650          }else if(s.parentNode){
34651             var newS = null;
34652             s.parentNode.bubble(function(){
34653                 if(this.nextSibling){
34654                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34655                     return false;
34656                 }
34657             });
34658             return newS;
34659          }
34660         return null;
34661     },
34662
34663     onKeyDown : function(e){
34664         var s = this.selNode || this.lastSelNode;
34665         // undesirable, but required
34666         var sm = this;
34667         if(!s){
34668             return;
34669         }
34670         var k = e.getKey();
34671         switch(k){
34672              case e.DOWN:
34673                  e.stopEvent();
34674                  this.selectNext();
34675              break;
34676              case e.UP:
34677                  e.stopEvent();
34678                  this.selectPrevious();
34679              break;
34680              case e.RIGHT:
34681                  e.preventDefault();
34682                  if(s.hasChildNodes()){
34683                      if(!s.isExpanded()){
34684                          s.expand();
34685                      }else if(s.firstChild){
34686                          this.select(s.firstChild, e);
34687                      }
34688                  }
34689              break;
34690              case e.LEFT:
34691                  e.preventDefault();
34692                  if(s.hasChildNodes() && s.isExpanded()){
34693                      s.collapse();
34694                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34695                      this.select(s.parentNode, e);
34696                  }
34697              break;
34698         };
34699     }
34700 });
34701
34702 /**
34703  * @class Roo.tree.MultiSelectionModel
34704  * @extends Roo.util.Observable
34705  * Multi selection for a TreePanel.
34706  * @param {Object} cfg Configuration
34707  */
34708 Roo.tree.MultiSelectionModel = function(){
34709    this.selNodes = [];
34710    this.selMap = {};
34711    this.addEvents({
34712        /**
34713         * @event selectionchange
34714         * Fires when the selected nodes change
34715         * @param {MultiSelectionModel} this
34716         * @param {Array} nodes Array of the selected nodes
34717         */
34718        "selectionchange" : true
34719    });
34720    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34721    
34722 };
34723
34724 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34725     init : function(tree){
34726         this.tree = tree;
34727         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34728         tree.on("click", this.onNodeClick, this);
34729     },
34730     
34731     onNodeClick : function(node, e){
34732         this.select(node, e, e.ctrlKey);
34733     },
34734     
34735     /**
34736      * Select a node.
34737      * @param {TreeNode} node The node to select
34738      * @param {EventObject} e (optional) An event associated with the selection
34739      * @param {Boolean} keepExisting True to retain existing selections
34740      * @return {TreeNode} The selected node
34741      */
34742     select : function(node, e, keepExisting){
34743         if(keepExisting !== true){
34744             this.clearSelections(true);
34745         }
34746         if(this.isSelected(node)){
34747             this.lastSelNode = node;
34748             return node;
34749         }
34750         this.selNodes.push(node);
34751         this.selMap[node.id] = node;
34752         this.lastSelNode = node;
34753         node.ui.onSelectedChange(true);
34754         this.fireEvent("selectionchange", this, this.selNodes);
34755         return node;
34756     },
34757     
34758     /**
34759      * Deselect a node.
34760      * @param {TreeNode} node The node to unselect
34761      */
34762     unselect : function(node){
34763         if(this.selMap[node.id]){
34764             node.ui.onSelectedChange(false);
34765             var sn = this.selNodes;
34766             var index = -1;
34767             if(sn.indexOf){
34768                 index = sn.indexOf(node);
34769             }else{
34770                 for(var i = 0, len = sn.length; i < len; i++){
34771                     if(sn[i] == node){
34772                         index = i;
34773                         break;
34774                     }
34775                 }
34776             }
34777             if(index != -1){
34778                 this.selNodes.splice(index, 1);
34779             }
34780             delete this.selMap[node.id];
34781             this.fireEvent("selectionchange", this, this.selNodes);
34782         }
34783     },
34784     
34785     /**
34786      * Clear all selections
34787      */
34788     clearSelections : function(suppressEvent){
34789         var sn = this.selNodes;
34790         if(sn.length > 0){
34791             for(var i = 0, len = sn.length; i < len; i++){
34792                 sn[i].ui.onSelectedChange(false);
34793             }
34794             this.selNodes = [];
34795             this.selMap = {};
34796             if(suppressEvent !== true){
34797                 this.fireEvent("selectionchange", this, this.selNodes);
34798             }
34799         }
34800     },
34801     
34802     /**
34803      * Returns true if the node is selected
34804      * @param {TreeNode} node The node to check
34805      * @return {Boolean}
34806      */
34807     isSelected : function(node){
34808         return this.selMap[node.id] ? true : false;  
34809     },
34810     
34811     /**
34812      * Returns an array of the selected nodes
34813      * @return {Array}
34814      */
34815     getSelectedNodes : function(){
34816         return this.selNodes;    
34817     },
34818
34819     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34820
34821     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34822
34823     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34824 });/*
34825  * Based on:
34826  * Ext JS Library 1.1.1
34827  * Copyright(c) 2006-2007, Ext JS, LLC.
34828  *
34829  * Originally Released Under LGPL - original licence link has changed is not relivant.
34830  *
34831  * Fork - LGPL
34832  * <script type="text/javascript">
34833  */
34834  
34835 /**
34836  * @class Roo.tree.TreeNode
34837  * @extends Roo.data.Node
34838  * @cfg {String} text The text for this node
34839  * @cfg {Boolean} expanded true to start the node expanded
34840  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34841  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34842  * @cfg {Boolean} disabled true to start the node disabled
34843  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34844  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34845  * @cfg {String} cls A css class to be added to the node
34846  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34847  * @cfg {String} href URL of the link used for the node (defaults to #)
34848  * @cfg {String} hrefTarget target frame for the link
34849  * @cfg {String} qtip An Ext QuickTip for the node
34850  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34851  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34852  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34853  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34854  * (defaults to undefined with no checkbox rendered)
34855  * @constructor
34856  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34857  */
34858 Roo.tree.TreeNode = function(attributes){
34859     attributes = attributes || {};
34860     if(typeof attributes == "string"){
34861         attributes = {text: attributes};
34862     }
34863     this.childrenRendered = false;
34864     this.rendered = false;
34865     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34866     this.expanded = attributes.expanded === true;
34867     this.isTarget = attributes.isTarget !== false;
34868     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34869     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34870
34871     /**
34872      * Read-only. The text for this node. To change it use setText().
34873      * @type String
34874      */
34875     this.text = attributes.text;
34876     /**
34877      * True if this node is disabled.
34878      * @type Boolean
34879      */
34880     this.disabled = attributes.disabled === true;
34881
34882     this.addEvents({
34883         /**
34884         * @event textchange
34885         * Fires when the text for this node is changed
34886         * @param {Node} this This node
34887         * @param {String} text The new text
34888         * @param {String} oldText The old text
34889         */
34890         "textchange" : true,
34891         /**
34892         * @event beforeexpand
34893         * Fires before this node is expanded, return false to cancel.
34894         * @param {Node} this This node
34895         * @param {Boolean} deep
34896         * @param {Boolean} anim
34897         */
34898         "beforeexpand" : true,
34899         /**
34900         * @event beforecollapse
34901         * Fires before this node is collapsed, return false to cancel.
34902         * @param {Node} this This node
34903         * @param {Boolean} deep
34904         * @param {Boolean} anim
34905         */
34906         "beforecollapse" : true,
34907         /**
34908         * @event expand
34909         * Fires when this node is expanded
34910         * @param {Node} this This node
34911         */
34912         "expand" : true,
34913         /**
34914         * @event disabledchange
34915         * Fires when the disabled status of this node changes
34916         * @param {Node} this This node
34917         * @param {Boolean} disabled
34918         */
34919         "disabledchange" : true,
34920         /**
34921         * @event collapse
34922         * Fires when this node is collapsed
34923         * @param {Node} this This node
34924         */
34925         "collapse" : true,
34926         /**
34927         * @event beforeclick
34928         * Fires before click processing. Return false to cancel the default action.
34929         * @param {Node} this This node
34930         * @param {Roo.EventObject} e The event object
34931         */
34932         "beforeclick":true,
34933         /**
34934         * @event checkchange
34935         * Fires when a node with a checkbox's checked property changes
34936         * @param {Node} this This node
34937         * @param {Boolean} checked
34938         */
34939         "checkchange":true,
34940         /**
34941         * @event click
34942         * Fires when this node is clicked
34943         * @param {Node} this This node
34944         * @param {Roo.EventObject} e The event object
34945         */
34946         "click":true,
34947         /**
34948         * @event dblclick
34949         * Fires when this node is double clicked
34950         * @param {Node} this This node
34951         * @param {Roo.EventObject} e The event object
34952         */
34953         "dblclick":true,
34954         /**
34955         * @event contextmenu
34956         * Fires when this node is right clicked
34957         * @param {Node} this This node
34958         * @param {Roo.EventObject} e The event object
34959         */
34960         "contextmenu":true,
34961         /**
34962         * @event beforechildrenrendered
34963         * Fires right before the child nodes for this node are rendered
34964         * @param {Node} this This node
34965         */
34966         "beforechildrenrendered":true
34967     });
34968
34969     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34970
34971     /**
34972      * Read-only. The UI for this node
34973      * @type TreeNodeUI
34974      */
34975     this.ui = new uiClass(this);
34976     
34977     // finally support items[]
34978     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34979         return;
34980     }
34981     
34982     
34983     Roo.each(this.attributes.items, function(c) {
34984         this.appendChild(Roo.factory(c,Roo.Tree));
34985     }, this);
34986     delete this.attributes.items;
34987     
34988     
34989     
34990 };
34991 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34992     preventHScroll: true,
34993     /**
34994      * Returns true if this node is expanded
34995      * @return {Boolean}
34996      */
34997     isExpanded : function(){
34998         return this.expanded;
34999     },
35000
35001     /**
35002      * Returns the UI object for this node
35003      * @return {TreeNodeUI}
35004      */
35005     getUI : function(){
35006         return this.ui;
35007     },
35008
35009     // private override
35010     setFirstChild : function(node){
35011         var of = this.firstChild;
35012         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35013         if(this.childrenRendered && of && node != of){
35014             of.renderIndent(true, true);
35015         }
35016         if(this.rendered){
35017             this.renderIndent(true, true);
35018         }
35019     },
35020
35021     // private override
35022     setLastChild : function(node){
35023         var ol = this.lastChild;
35024         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35025         if(this.childrenRendered && ol && node != ol){
35026             ol.renderIndent(true, true);
35027         }
35028         if(this.rendered){
35029             this.renderIndent(true, true);
35030         }
35031     },
35032
35033     // these methods are overridden to provide lazy rendering support
35034     // private override
35035     appendChild : function()
35036     {
35037         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35038         if(node && this.childrenRendered){
35039             node.render();
35040         }
35041         this.ui.updateExpandIcon();
35042         return node;
35043     },
35044
35045     // private override
35046     removeChild : function(node){
35047         this.ownerTree.getSelectionModel().unselect(node);
35048         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35049         // if it's been rendered remove dom node
35050         if(this.childrenRendered){
35051             node.ui.remove();
35052         }
35053         if(this.childNodes.length < 1){
35054             this.collapse(false, false);
35055         }else{
35056             this.ui.updateExpandIcon();
35057         }
35058         if(!this.firstChild) {
35059             this.childrenRendered = false;
35060         }
35061         return node;
35062     },
35063
35064     // private override
35065     insertBefore : function(node, refNode){
35066         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35067         if(newNode && refNode && this.childrenRendered){
35068             node.render();
35069         }
35070         this.ui.updateExpandIcon();
35071         return newNode;
35072     },
35073
35074     /**
35075      * Sets the text for this node
35076      * @param {String} text
35077      */
35078     setText : function(text){
35079         var oldText = this.text;
35080         this.text = text;
35081         this.attributes.text = text;
35082         if(this.rendered){ // event without subscribing
35083             this.ui.onTextChange(this, text, oldText);
35084         }
35085         this.fireEvent("textchange", this, text, oldText);
35086     },
35087
35088     /**
35089      * Triggers selection of this node
35090      */
35091     select : function(){
35092         this.getOwnerTree().getSelectionModel().select(this);
35093     },
35094
35095     /**
35096      * Triggers deselection of this node
35097      */
35098     unselect : function(){
35099         this.getOwnerTree().getSelectionModel().unselect(this);
35100     },
35101
35102     /**
35103      * Returns true if this node is selected
35104      * @return {Boolean}
35105      */
35106     isSelected : function(){
35107         return this.getOwnerTree().getSelectionModel().isSelected(this);
35108     },
35109
35110     /**
35111      * Expand this node.
35112      * @param {Boolean} deep (optional) True to expand all children as well
35113      * @param {Boolean} anim (optional) false to cancel the default animation
35114      * @param {Function} callback (optional) A callback to be called when
35115      * expanding this node completes (does not wait for deep expand to complete).
35116      * Called with 1 parameter, this node.
35117      */
35118     expand : function(deep, anim, callback){
35119         if(!this.expanded){
35120             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35121                 return;
35122             }
35123             if(!this.childrenRendered){
35124                 this.renderChildren();
35125             }
35126             this.expanded = true;
35127             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
35128                 this.ui.animExpand(function(){
35129                     this.fireEvent("expand", this);
35130                     if(typeof callback == "function"){
35131                         callback(this);
35132                     }
35133                     if(deep === true){
35134                         this.expandChildNodes(true);
35135                     }
35136                 }.createDelegate(this));
35137                 return;
35138             }else{
35139                 this.ui.expand();
35140                 this.fireEvent("expand", this);
35141                 if(typeof callback == "function"){
35142                     callback(this);
35143                 }
35144             }
35145         }else{
35146            if(typeof callback == "function"){
35147                callback(this);
35148            }
35149         }
35150         if(deep === true){
35151             this.expandChildNodes(true);
35152         }
35153     },
35154
35155     isHiddenRoot : function(){
35156         return this.isRoot && !this.getOwnerTree().rootVisible;
35157     },
35158
35159     /**
35160      * Collapse this node.
35161      * @param {Boolean} deep (optional) True to collapse all children as well
35162      * @param {Boolean} anim (optional) false to cancel the default animation
35163      */
35164     collapse : function(deep, anim){
35165         if(this.expanded && !this.isHiddenRoot()){
35166             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35167                 return;
35168             }
35169             this.expanded = false;
35170             if((this.getOwnerTree().animate && anim !== false) || anim){
35171                 this.ui.animCollapse(function(){
35172                     this.fireEvent("collapse", this);
35173                     if(deep === true){
35174                         this.collapseChildNodes(true);
35175                     }
35176                 }.createDelegate(this));
35177                 return;
35178             }else{
35179                 this.ui.collapse();
35180                 this.fireEvent("collapse", this);
35181             }
35182         }
35183         if(deep === true){
35184             var cs = this.childNodes;
35185             for(var i = 0, len = cs.length; i < len; i++) {
35186                 cs[i].collapse(true, false);
35187             }
35188         }
35189     },
35190
35191     // private
35192     delayedExpand : function(delay){
35193         if(!this.expandProcId){
35194             this.expandProcId = this.expand.defer(delay, this);
35195         }
35196     },
35197
35198     // private
35199     cancelExpand : function(){
35200         if(this.expandProcId){
35201             clearTimeout(this.expandProcId);
35202         }
35203         this.expandProcId = false;
35204     },
35205
35206     /**
35207      * Toggles expanded/collapsed state of the node
35208      */
35209     toggle : function(){
35210         if(this.expanded){
35211             this.collapse();
35212         }else{
35213             this.expand();
35214         }
35215     },
35216
35217     /**
35218      * Ensures all parent nodes are expanded
35219      */
35220     ensureVisible : function(callback){
35221         var tree = this.getOwnerTree();
35222         tree.expandPath(this.parentNode.getPath(), false, function(){
35223             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35224             Roo.callback(callback);
35225         }.createDelegate(this));
35226     },
35227
35228     /**
35229      * Expand all child nodes
35230      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35231      */
35232     expandChildNodes : function(deep){
35233         var cs = this.childNodes;
35234         for(var i = 0, len = cs.length; i < len; i++) {
35235                 cs[i].expand(deep);
35236         }
35237     },
35238
35239     /**
35240      * Collapse all child nodes
35241      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35242      */
35243     collapseChildNodes : function(deep){
35244         var cs = this.childNodes;
35245         for(var i = 0, len = cs.length; i < len; i++) {
35246                 cs[i].collapse(deep);
35247         }
35248     },
35249
35250     /**
35251      * Disables this node
35252      */
35253     disable : function(){
35254         this.disabled = true;
35255         this.unselect();
35256         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35257             this.ui.onDisableChange(this, true);
35258         }
35259         this.fireEvent("disabledchange", this, true);
35260     },
35261
35262     /**
35263      * Enables this node
35264      */
35265     enable : function(){
35266         this.disabled = false;
35267         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35268             this.ui.onDisableChange(this, false);
35269         }
35270         this.fireEvent("disabledchange", this, false);
35271     },
35272
35273     // private
35274     renderChildren : function(suppressEvent){
35275         if(suppressEvent !== false){
35276             this.fireEvent("beforechildrenrendered", this);
35277         }
35278         var cs = this.childNodes;
35279         for(var i = 0, len = cs.length; i < len; i++){
35280             cs[i].render(true);
35281         }
35282         this.childrenRendered = true;
35283     },
35284
35285     // private
35286     sort : function(fn, scope){
35287         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35288         if(this.childrenRendered){
35289             var cs = this.childNodes;
35290             for(var i = 0, len = cs.length; i < len; i++){
35291                 cs[i].render(true);
35292             }
35293         }
35294     },
35295
35296     // private
35297     render : function(bulkRender){
35298         this.ui.render(bulkRender);
35299         if(!this.rendered){
35300             this.rendered = true;
35301             if(this.expanded){
35302                 this.expanded = false;
35303                 this.expand(false, false);
35304             }
35305         }
35306     },
35307
35308     // private
35309     renderIndent : function(deep, refresh){
35310         if(refresh){
35311             this.ui.childIndent = null;
35312         }
35313         this.ui.renderIndent();
35314         if(deep === true && this.childrenRendered){
35315             var cs = this.childNodes;
35316             for(var i = 0, len = cs.length; i < len; i++){
35317                 cs[i].renderIndent(true, refresh);
35318             }
35319         }
35320     }
35321 });/*
35322  * Based on:
35323  * Ext JS Library 1.1.1
35324  * Copyright(c) 2006-2007, Ext JS, LLC.
35325  *
35326  * Originally Released Under LGPL - original licence link has changed is not relivant.
35327  *
35328  * Fork - LGPL
35329  * <script type="text/javascript">
35330  */
35331  
35332 /**
35333  * @class Roo.tree.AsyncTreeNode
35334  * @extends Roo.tree.TreeNode
35335  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35336  * @constructor
35337  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35338  */
35339  Roo.tree.AsyncTreeNode = function(config){
35340     this.loaded = false;
35341     this.loading = false;
35342     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35343     /**
35344     * @event beforeload
35345     * Fires before this node is loaded, return false to cancel
35346     * @param {Node} this This node
35347     */
35348     this.addEvents({'beforeload':true, 'load': true});
35349     /**
35350     * @event load
35351     * Fires when this node is loaded
35352     * @param {Node} this This node
35353     */
35354     /**
35355      * The loader used by this node (defaults to using the tree's defined loader)
35356      * @type TreeLoader
35357      * @property loader
35358      */
35359 };
35360 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35361     expand : function(deep, anim, callback){
35362         if(this.loading){ // if an async load is already running, waiting til it's done
35363             var timer;
35364             var f = function(){
35365                 if(!this.loading){ // done loading
35366                     clearInterval(timer);
35367                     this.expand(deep, anim, callback);
35368                 }
35369             }.createDelegate(this);
35370             timer = setInterval(f, 200);
35371             return;
35372         }
35373         if(!this.loaded){
35374             if(this.fireEvent("beforeload", this) === false){
35375                 return;
35376             }
35377             this.loading = true;
35378             this.ui.beforeLoad(this);
35379             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35380             if(loader){
35381                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35382                 return;
35383             }
35384         }
35385         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35386     },
35387     
35388     /**
35389      * Returns true if this node is currently loading
35390      * @return {Boolean}
35391      */
35392     isLoading : function(){
35393         return this.loading;  
35394     },
35395     
35396     loadComplete : function(deep, anim, callback){
35397         this.loading = false;
35398         this.loaded = true;
35399         this.ui.afterLoad(this);
35400         this.fireEvent("load", this);
35401         this.expand(deep, anim, callback);
35402     },
35403     
35404     /**
35405      * Returns true if this node has been loaded
35406      * @return {Boolean}
35407      */
35408     isLoaded : function(){
35409         return this.loaded;
35410     },
35411     
35412     hasChildNodes : function(){
35413         if(!this.isLeaf() && !this.loaded){
35414             return true;
35415         }else{
35416             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35417         }
35418     },
35419
35420     /**
35421      * Trigger a reload for this node
35422      * @param {Function} callback
35423      */
35424     reload : function(callback){
35425         this.collapse(false, false);
35426         while(this.firstChild){
35427             this.removeChild(this.firstChild);
35428         }
35429         this.childrenRendered = false;
35430         this.loaded = false;
35431         if(this.isHiddenRoot()){
35432             this.expanded = false;
35433         }
35434         this.expand(false, false, callback);
35435     }
35436 });/*
35437  * Based on:
35438  * Ext JS Library 1.1.1
35439  * Copyright(c) 2006-2007, Ext JS, LLC.
35440  *
35441  * Originally Released Under LGPL - original licence link has changed is not relivant.
35442  *
35443  * Fork - LGPL
35444  * <script type="text/javascript">
35445  */
35446  
35447 /**
35448  * @class Roo.tree.TreeNodeUI
35449  * @constructor
35450  * @param {Object} node The node to render
35451  * The TreeNode UI implementation is separate from the
35452  * tree implementation. Unless you are customizing the tree UI,
35453  * you should never have to use this directly.
35454  */
35455 Roo.tree.TreeNodeUI = function(node){
35456     this.node = node;
35457     this.rendered = false;
35458     this.animating = false;
35459     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35460 };
35461
35462 Roo.tree.TreeNodeUI.prototype = {
35463     removeChild : function(node){
35464         if(this.rendered){
35465             this.ctNode.removeChild(node.ui.getEl());
35466         }
35467     },
35468
35469     beforeLoad : function(){
35470          this.addClass("x-tree-node-loading");
35471     },
35472
35473     afterLoad : function(){
35474          this.removeClass("x-tree-node-loading");
35475     },
35476
35477     onTextChange : function(node, text, oldText){
35478         if(this.rendered){
35479             this.textNode.innerHTML = text;
35480         }
35481     },
35482
35483     onDisableChange : function(node, state){
35484         this.disabled = state;
35485         if(state){
35486             this.addClass("x-tree-node-disabled");
35487         }else{
35488             this.removeClass("x-tree-node-disabled");
35489         }
35490     },
35491
35492     onSelectedChange : function(state){
35493         if(state){
35494             this.focus();
35495             this.addClass("x-tree-selected");
35496         }else{
35497             //this.blur();
35498             this.removeClass("x-tree-selected");
35499         }
35500     },
35501
35502     onMove : function(tree, node, oldParent, newParent, index, refNode){
35503         this.childIndent = null;
35504         if(this.rendered){
35505             var targetNode = newParent.ui.getContainer();
35506             if(!targetNode){//target not rendered
35507                 this.holder = document.createElement("div");
35508                 this.holder.appendChild(this.wrap);
35509                 return;
35510             }
35511             var insertBefore = refNode ? refNode.ui.getEl() : null;
35512             if(insertBefore){
35513                 targetNode.insertBefore(this.wrap, insertBefore);
35514             }else{
35515                 targetNode.appendChild(this.wrap);
35516             }
35517             this.node.renderIndent(true);
35518         }
35519     },
35520
35521     addClass : function(cls){
35522         if(this.elNode){
35523             Roo.fly(this.elNode).addClass(cls);
35524         }
35525     },
35526
35527     removeClass : function(cls){
35528         if(this.elNode){
35529             Roo.fly(this.elNode).removeClass(cls);
35530         }
35531     },
35532
35533     remove : function(){
35534         if(this.rendered){
35535             this.holder = document.createElement("div");
35536             this.holder.appendChild(this.wrap);
35537         }
35538     },
35539
35540     fireEvent : function(){
35541         return this.node.fireEvent.apply(this.node, arguments);
35542     },
35543
35544     initEvents : function(){
35545         this.node.on("move", this.onMove, this);
35546         var E = Roo.EventManager;
35547         var a = this.anchor;
35548
35549         var el = Roo.fly(a, '_treeui');
35550
35551         if(Roo.isOpera){ // opera render bug ignores the CSS
35552             el.setStyle("text-decoration", "none");
35553         }
35554
35555         el.on("click", this.onClick, this);
35556         el.on("dblclick", this.onDblClick, this);
35557
35558         if(this.checkbox){
35559             Roo.EventManager.on(this.checkbox,
35560                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35561         }
35562
35563         el.on("contextmenu", this.onContextMenu, this);
35564
35565         var icon = Roo.fly(this.iconNode);
35566         icon.on("click", this.onClick, this);
35567         icon.on("dblclick", this.onDblClick, this);
35568         icon.on("contextmenu", this.onContextMenu, this);
35569         E.on(this.ecNode, "click", this.ecClick, this, true);
35570
35571         if(this.node.disabled){
35572             this.addClass("x-tree-node-disabled");
35573         }
35574         if(this.node.hidden){
35575             this.addClass("x-tree-node-disabled");
35576         }
35577         var ot = this.node.getOwnerTree();
35578         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35579         if(dd && (!this.node.isRoot || ot.rootVisible)){
35580             Roo.dd.Registry.register(this.elNode, {
35581                 node: this.node,
35582                 handles: this.getDDHandles(),
35583                 isHandle: false
35584             });
35585         }
35586     },
35587
35588     getDDHandles : function(){
35589         return [this.iconNode, this.textNode];
35590     },
35591
35592     hide : function(){
35593         if(this.rendered){
35594             this.wrap.style.display = "none";
35595         }
35596     },
35597
35598     show : function(){
35599         if(this.rendered){
35600             this.wrap.style.display = "";
35601         }
35602     },
35603
35604     onContextMenu : function(e){
35605         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35606             e.preventDefault();
35607             this.focus();
35608             this.fireEvent("contextmenu", this.node, e);
35609         }
35610     },
35611
35612     onClick : function(e){
35613         if(this.dropping){
35614             e.stopEvent();
35615             return;
35616         }
35617         if(this.fireEvent("beforeclick", this.node, e) !== false){
35618             if(!this.disabled && this.node.attributes.href){
35619                 this.fireEvent("click", this.node, e);
35620                 return;
35621             }
35622             e.preventDefault();
35623             if(this.disabled){
35624                 return;
35625             }
35626
35627             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35628                 this.node.toggle();
35629             }
35630
35631             this.fireEvent("click", this.node, e);
35632         }else{
35633             e.stopEvent();
35634         }
35635     },
35636
35637     onDblClick : function(e){
35638         e.preventDefault();
35639         if(this.disabled){
35640             return;
35641         }
35642         if(this.checkbox){
35643             this.toggleCheck();
35644         }
35645         if(!this.animating && this.node.hasChildNodes()){
35646             this.node.toggle();
35647         }
35648         this.fireEvent("dblclick", this.node, e);
35649     },
35650
35651     onCheckChange : function(){
35652         var checked = this.checkbox.checked;
35653         this.node.attributes.checked = checked;
35654         this.fireEvent('checkchange', this.node, checked);
35655     },
35656
35657     ecClick : function(e){
35658         if(!this.animating && this.node.hasChildNodes()){
35659             this.node.toggle();
35660         }
35661     },
35662
35663     startDrop : function(){
35664         this.dropping = true;
35665     },
35666
35667     // delayed drop so the click event doesn't get fired on a drop
35668     endDrop : function(){
35669        setTimeout(function(){
35670            this.dropping = false;
35671        }.createDelegate(this), 50);
35672     },
35673
35674     expand : function(){
35675         this.updateExpandIcon();
35676         this.ctNode.style.display = "";
35677     },
35678
35679     focus : function(){
35680         if(!this.node.preventHScroll){
35681             try{this.anchor.focus();
35682             }catch(e){}
35683         }else if(!Roo.isIE){
35684             try{
35685                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35686                 var l = noscroll.scrollLeft;
35687                 this.anchor.focus();
35688                 noscroll.scrollLeft = l;
35689             }catch(e){}
35690         }
35691     },
35692
35693     toggleCheck : function(value){
35694         var cb = this.checkbox;
35695         if(cb){
35696             cb.checked = (value === undefined ? !cb.checked : value);
35697         }
35698     },
35699
35700     blur : function(){
35701         try{
35702             this.anchor.blur();
35703         }catch(e){}
35704     },
35705
35706     animExpand : function(callback){
35707         var ct = Roo.get(this.ctNode);
35708         ct.stopFx();
35709         if(!this.node.hasChildNodes()){
35710             this.updateExpandIcon();
35711             this.ctNode.style.display = "";
35712             Roo.callback(callback);
35713             return;
35714         }
35715         this.animating = true;
35716         this.updateExpandIcon();
35717
35718         ct.slideIn('t', {
35719            callback : function(){
35720                this.animating = false;
35721                Roo.callback(callback);
35722             },
35723             scope: this,
35724             duration: this.node.ownerTree.duration || .25
35725         });
35726     },
35727
35728     highlight : function(){
35729         var tree = this.node.getOwnerTree();
35730         Roo.fly(this.wrap).highlight(
35731             tree.hlColor || "C3DAF9",
35732             {endColor: tree.hlBaseColor}
35733         );
35734     },
35735
35736     collapse : function(){
35737         this.updateExpandIcon();
35738         this.ctNode.style.display = "none";
35739     },
35740
35741     animCollapse : function(callback){
35742         var ct = Roo.get(this.ctNode);
35743         ct.enableDisplayMode('block');
35744         ct.stopFx();
35745
35746         this.animating = true;
35747         this.updateExpandIcon();
35748
35749         ct.slideOut('t', {
35750             callback : function(){
35751                this.animating = false;
35752                Roo.callback(callback);
35753             },
35754             scope: this,
35755             duration: this.node.ownerTree.duration || .25
35756         });
35757     },
35758
35759     getContainer : function(){
35760         return this.ctNode;
35761     },
35762
35763     getEl : function(){
35764         return this.wrap;
35765     },
35766
35767     appendDDGhost : function(ghostNode){
35768         ghostNode.appendChild(this.elNode.cloneNode(true));
35769     },
35770
35771     getDDRepairXY : function(){
35772         return Roo.lib.Dom.getXY(this.iconNode);
35773     },
35774
35775     onRender : function(){
35776         this.render();
35777     },
35778
35779     render : function(bulkRender){
35780         var n = this.node, a = n.attributes;
35781         var targetNode = n.parentNode ?
35782               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35783
35784         if(!this.rendered){
35785             this.rendered = true;
35786
35787             this.renderElements(n, a, targetNode, bulkRender);
35788
35789             if(a.qtip){
35790                if(this.textNode.setAttributeNS){
35791                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35792                    if(a.qtipTitle){
35793                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35794                    }
35795                }else{
35796                    this.textNode.setAttribute("ext:qtip", a.qtip);
35797                    if(a.qtipTitle){
35798                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35799                    }
35800                }
35801             }else if(a.qtipCfg){
35802                 a.qtipCfg.target = Roo.id(this.textNode);
35803                 Roo.QuickTips.register(a.qtipCfg);
35804             }
35805             this.initEvents();
35806             if(!this.node.expanded){
35807                 this.updateExpandIcon();
35808             }
35809         }else{
35810             if(bulkRender === true) {
35811                 targetNode.appendChild(this.wrap);
35812             }
35813         }
35814     },
35815
35816     renderElements : function(n, a, targetNode, bulkRender)
35817     {
35818         // add some indent caching, this helps performance when rendering a large tree
35819         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35820         var t = n.getOwnerTree();
35821         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35822         if (typeof(n.attributes.html) != 'undefined') {
35823             txt = n.attributes.html;
35824         }
35825         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35826         var cb = typeof a.checked == 'boolean';
35827         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35828         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35829             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35830             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35831             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35832             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35833             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35834              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35835                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35836             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35837             "</li>"];
35838
35839         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35840             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35841                                 n.nextSibling.ui.getEl(), buf.join(""));
35842         }else{
35843             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35844         }
35845
35846         this.elNode = this.wrap.childNodes[0];
35847         this.ctNode = this.wrap.childNodes[1];
35848         var cs = this.elNode.childNodes;
35849         this.indentNode = cs[0];
35850         this.ecNode = cs[1];
35851         this.iconNode = cs[2];
35852         var index = 3;
35853         if(cb){
35854             this.checkbox = cs[3];
35855             index++;
35856         }
35857         this.anchor = cs[index];
35858         this.textNode = cs[index].firstChild;
35859     },
35860
35861     getAnchor : function(){
35862         return this.anchor;
35863     },
35864
35865     getTextEl : function(){
35866         return this.textNode;
35867     },
35868
35869     getIconEl : function(){
35870         return this.iconNode;
35871     },
35872
35873     isChecked : function(){
35874         return this.checkbox ? this.checkbox.checked : false;
35875     },
35876
35877     updateExpandIcon : function(){
35878         if(this.rendered){
35879             var n = this.node, c1, c2;
35880             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35881             var hasChild = n.hasChildNodes();
35882             if(hasChild){
35883                 if(n.expanded){
35884                     cls += "-minus";
35885                     c1 = "x-tree-node-collapsed";
35886                     c2 = "x-tree-node-expanded";
35887                 }else{
35888                     cls += "-plus";
35889                     c1 = "x-tree-node-expanded";
35890                     c2 = "x-tree-node-collapsed";
35891                 }
35892                 if(this.wasLeaf){
35893                     this.removeClass("x-tree-node-leaf");
35894                     this.wasLeaf = false;
35895                 }
35896                 if(this.c1 != c1 || this.c2 != c2){
35897                     Roo.fly(this.elNode).replaceClass(c1, c2);
35898                     this.c1 = c1; this.c2 = c2;
35899                 }
35900             }else{
35901                 // this changes non-leafs into leafs if they have no children.
35902                 // it's not very rational behaviour..
35903                 
35904                 if(!this.wasLeaf && this.node.leaf){
35905                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35906                     delete this.c1;
35907                     delete this.c2;
35908                     this.wasLeaf = true;
35909                 }
35910             }
35911             var ecc = "x-tree-ec-icon "+cls;
35912             if(this.ecc != ecc){
35913                 this.ecNode.className = ecc;
35914                 this.ecc = ecc;
35915             }
35916         }
35917     },
35918
35919     getChildIndent : function(){
35920         if(!this.childIndent){
35921             var buf = [];
35922             var p = this.node;
35923             while(p){
35924                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35925                     if(!p.isLast()) {
35926                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35927                     } else {
35928                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35929                     }
35930                 }
35931                 p = p.parentNode;
35932             }
35933             this.childIndent = buf.join("");
35934         }
35935         return this.childIndent;
35936     },
35937
35938     renderIndent : function(){
35939         if(this.rendered){
35940             var indent = "";
35941             var p = this.node.parentNode;
35942             if(p){
35943                 indent = p.ui.getChildIndent();
35944             }
35945             if(this.indentMarkup != indent){ // don't rerender if not required
35946                 this.indentNode.innerHTML = indent;
35947                 this.indentMarkup = indent;
35948             }
35949             this.updateExpandIcon();
35950         }
35951     }
35952 };
35953
35954 Roo.tree.RootTreeNodeUI = function(){
35955     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35956 };
35957 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35958     render : function(){
35959         if(!this.rendered){
35960             var targetNode = this.node.ownerTree.innerCt.dom;
35961             this.node.expanded = true;
35962             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35963             this.wrap = this.ctNode = targetNode.firstChild;
35964         }
35965     },
35966     collapse : function(){
35967     },
35968     expand : function(){
35969     }
35970 });/*
35971  * Based on:
35972  * Ext JS Library 1.1.1
35973  * Copyright(c) 2006-2007, Ext JS, LLC.
35974  *
35975  * Originally Released Under LGPL - original licence link has changed is not relivant.
35976  *
35977  * Fork - LGPL
35978  * <script type="text/javascript">
35979  */
35980 /**
35981  * @class Roo.tree.TreeLoader
35982  * @extends Roo.util.Observable
35983  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35984  * nodes from a specified URL. The response must be a javascript Array definition
35985  * who's elements are node definition objects. eg:
35986  * <pre><code>
35987 {  success : true,
35988    data :      [
35989    
35990     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35991     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35992     ]
35993 }
35994
35995
35996 </code></pre>
35997  * <br><br>
35998  * The old style respose with just an array is still supported, but not recommended.
35999  * <br><br>
36000  *
36001  * A server request is sent, and child nodes are loaded only when a node is expanded.
36002  * The loading node's id is passed to the server under the parameter name "node" to
36003  * enable the server to produce the correct child nodes.
36004  * <br><br>
36005  * To pass extra parameters, an event handler may be attached to the "beforeload"
36006  * event, and the parameters specified in the TreeLoader's baseParams property:
36007  * <pre><code>
36008     myTreeLoader.on("beforeload", function(treeLoader, node) {
36009         this.baseParams.category = node.attributes.category;
36010     }, this);
36011 </code></pre><
36012  * This would pass an HTTP parameter called "category" to the server containing
36013  * the value of the Node's "category" attribute.
36014  * @constructor
36015  * Creates a new Treeloader.
36016  * @param {Object} config A config object containing config properties.
36017  */
36018 Roo.tree.TreeLoader = function(config){
36019     this.baseParams = {};
36020     this.requestMethod = "POST";
36021     Roo.apply(this, config);
36022
36023     this.addEvents({
36024     
36025         /**
36026          * @event beforeload
36027          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36028          * @param {Object} This TreeLoader object.
36029          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36030          * @param {Object} callback The callback function specified in the {@link #load} call.
36031          */
36032         beforeload : true,
36033         /**
36034          * @event load
36035          * Fires when the node has been successfuly loaded.
36036          * @param {Object} This TreeLoader object.
36037          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36038          * @param {Object} response The response object containing the data from the server.
36039          */
36040         load : true,
36041         /**
36042          * @event loadexception
36043          * Fires if the network request failed.
36044          * @param {Object} This TreeLoader object.
36045          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36046          * @param {Object} response The response object containing the data from the server.
36047          */
36048         loadexception : true,
36049         /**
36050          * @event create
36051          * Fires before a node is created, enabling you to return custom Node types 
36052          * @param {Object} This TreeLoader object.
36053          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36054          */
36055         create : true
36056     });
36057
36058     Roo.tree.TreeLoader.superclass.constructor.call(this);
36059 };
36060
36061 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36062     /**
36063     * @cfg {String} dataUrl The URL from which to request a Json string which
36064     * specifies an array of node definition object representing the child nodes
36065     * to be loaded.
36066     */
36067     /**
36068     * @cfg {String} requestMethod either GET or POST
36069     * defaults to POST (due to BC)
36070     * to be loaded.
36071     */
36072     /**
36073     * @cfg {Object} baseParams (optional) An object containing properties which
36074     * specify HTTP parameters to be passed to each request for child nodes.
36075     */
36076     /**
36077     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36078     * created by this loader. If the attributes sent by the server have an attribute in this object,
36079     * they take priority.
36080     */
36081     /**
36082     * @cfg {Object} uiProviders (optional) An object containing properties which
36083     * 
36084     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36085     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36086     * <i>uiProvider</i> attribute of a returned child node is a string rather
36087     * than a reference to a TreeNodeUI implementation, this that string value
36088     * is used as a property name in the uiProviders object. You can define the provider named
36089     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36090     */
36091     uiProviders : {},
36092
36093     /**
36094     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36095     * child nodes before loading.
36096     */
36097     clearOnLoad : true,
36098
36099     /**
36100     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36101     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36102     * Grid query { data : [ .....] }
36103     */
36104     
36105     root : false,
36106      /**
36107     * @cfg {String} queryParam (optional) 
36108     * Name of the query as it will be passed on the querystring (defaults to 'node')
36109     * eg. the request will be ?node=[id]
36110     */
36111     
36112     
36113     queryParam: false,
36114     
36115     /**
36116      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36117      * This is called automatically when a node is expanded, but may be used to reload
36118      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36119      * @param {Roo.tree.TreeNode} node
36120      * @param {Function} callback
36121      */
36122     load : function(node, callback){
36123         if(this.clearOnLoad){
36124             while(node.firstChild){
36125                 node.removeChild(node.firstChild);
36126             }
36127         }
36128         if(node.attributes.children){ // preloaded json children
36129             var cs = node.attributes.children;
36130             for(var i = 0, len = cs.length; i < len; i++){
36131                 node.appendChild(this.createNode(cs[i]));
36132             }
36133             if(typeof callback == "function"){
36134                 callback();
36135             }
36136         }else if(this.dataUrl){
36137             this.requestData(node, callback);
36138         }
36139     },
36140
36141     getParams: function(node){
36142         var buf = [], bp = this.baseParams;
36143         for(var key in bp){
36144             if(typeof bp[key] != "function"){
36145                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36146             }
36147         }
36148         var n = this.queryParam === false ? 'node' : this.queryParam;
36149         buf.push(n + "=", encodeURIComponent(node.id));
36150         return buf.join("");
36151     },
36152
36153     requestData : function(node, callback){
36154         if(this.fireEvent("beforeload", this, node, callback) !== false){
36155             this.transId = Roo.Ajax.request({
36156                 method:this.requestMethod,
36157                 url: this.dataUrl||this.url,
36158                 success: this.handleResponse,
36159                 failure: this.handleFailure,
36160                 scope: this,
36161                 argument: {callback: callback, node: node},
36162                 params: this.getParams(node)
36163             });
36164         }else{
36165             // if the load is cancelled, make sure we notify
36166             // the node that we are done
36167             if(typeof callback == "function"){
36168                 callback();
36169             }
36170         }
36171     },
36172
36173     isLoading : function(){
36174         return this.transId ? true : false;
36175     },
36176
36177     abort : function(){
36178         if(this.isLoading()){
36179             Roo.Ajax.abort(this.transId);
36180         }
36181     },
36182
36183     // private
36184     createNode : function(attr)
36185     {
36186         // apply baseAttrs, nice idea Corey!
36187         if(this.baseAttrs){
36188             Roo.applyIf(attr, this.baseAttrs);
36189         }
36190         if(this.applyLoader !== false){
36191             attr.loader = this;
36192         }
36193         // uiProvider = depreciated..
36194         
36195         if(typeof(attr.uiProvider) == 'string'){
36196            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36197                 /**  eval:var:attr */ eval(attr.uiProvider);
36198         }
36199         if(typeof(this.uiProviders['default']) != 'undefined') {
36200             attr.uiProvider = this.uiProviders['default'];
36201         }
36202         
36203         this.fireEvent('create', this, attr);
36204         
36205         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36206         return(attr.leaf ?
36207                         new Roo.tree.TreeNode(attr) :
36208                         new Roo.tree.AsyncTreeNode(attr));
36209     },
36210
36211     processResponse : function(response, node, callback)
36212     {
36213         var json = response.responseText;
36214         try {
36215             
36216             var o = Roo.decode(json);
36217             
36218             if (this.root === false && typeof(o.success) != undefined) {
36219                 this.root = 'data'; // the default behaviour for list like data..
36220                 }
36221                 
36222             if (this.root !== false &&  !o.success) {
36223                 // it's a failure condition.
36224                 var a = response.argument;
36225                 this.fireEvent("loadexception", this, a.node, response);
36226                 Roo.log("Load failed - should have a handler really");
36227                 return;
36228             }
36229             
36230             
36231             
36232             if (this.root !== false) {
36233                  o = o[this.root];
36234             }
36235             
36236             for(var i = 0, len = o.length; i < len; i++){
36237                 var n = this.createNode(o[i]);
36238                 if(n){
36239                     node.appendChild(n);
36240                 }
36241             }
36242             if(typeof callback == "function"){
36243                 callback(this, node);
36244             }
36245         }catch(e){
36246             this.handleFailure(response);
36247         }
36248     },
36249
36250     handleResponse : function(response){
36251         this.transId = false;
36252         var a = response.argument;
36253         this.processResponse(response, a.node, a.callback);
36254         this.fireEvent("load", this, a.node, response);
36255     },
36256
36257     handleFailure : function(response)
36258     {
36259         // should handle failure better..
36260         this.transId = false;
36261         var a = response.argument;
36262         this.fireEvent("loadexception", this, a.node, response);
36263         if(typeof a.callback == "function"){
36264             a.callback(this, a.node);
36265         }
36266     }
36267 });/*
36268  * Based on:
36269  * Ext JS Library 1.1.1
36270  * Copyright(c) 2006-2007, Ext JS, LLC.
36271  *
36272  * Originally Released Under LGPL - original licence link has changed is not relivant.
36273  *
36274  * Fork - LGPL
36275  * <script type="text/javascript">
36276  */
36277
36278 /**
36279 * @class Roo.tree.TreeFilter
36280 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36281 * @param {TreePanel} tree
36282 * @param {Object} config (optional)
36283  */
36284 Roo.tree.TreeFilter = function(tree, config){
36285     this.tree = tree;
36286     this.filtered = {};
36287     Roo.apply(this, config);
36288 };
36289
36290 Roo.tree.TreeFilter.prototype = {
36291     clearBlank:false,
36292     reverse:false,
36293     autoClear:false,
36294     remove:false,
36295
36296      /**
36297      * Filter the data by a specific attribute.
36298      * @param {String/RegExp} value Either string that the attribute value
36299      * should start with or a RegExp to test against the attribute
36300      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36301      * @param {TreeNode} startNode (optional) The node to start the filter at.
36302      */
36303     filter : function(value, attr, startNode){
36304         attr = attr || "text";
36305         var f;
36306         if(typeof value == "string"){
36307             var vlen = value.length;
36308             // auto clear empty filter
36309             if(vlen == 0 && this.clearBlank){
36310                 this.clear();
36311                 return;
36312             }
36313             value = value.toLowerCase();
36314             f = function(n){
36315                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36316             };
36317         }else if(value.exec){ // regex?
36318             f = function(n){
36319                 return value.test(n.attributes[attr]);
36320             };
36321         }else{
36322             throw 'Illegal filter type, must be string or regex';
36323         }
36324         this.filterBy(f, null, startNode);
36325         },
36326
36327     /**
36328      * Filter by a function. The passed function will be called with each
36329      * node in the tree (or from the startNode). If the function returns true, the node is kept
36330      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36331      * @param {Function} fn The filter function
36332      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36333      */
36334     filterBy : function(fn, scope, startNode){
36335         startNode = startNode || this.tree.root;
36336         if(this.autoClear){
36337             this.clear();
36338         }
36339         var af = this.filtered, rv = this.reverse;
36340         var f = function(n){
36341             if(n == startNode){
36342                 return true;
36343             }
36344             if(af[n.id]){
36345                 return false;
36346             }
36347             var m = fn.call(scope || n, n);
36348             if(!m || rv){
36349                 af[n.id] = n;
36350                 n.ui.hide();
36351                 return false;
36352             }
36353             return true;
36354         };
36355         startNode.cascade(f);
36356         if(this.remove){
36357            for(var id in af){
36358                if(typeof id != "function"){
36359                    var n = af[id];
36360                    if(n && n.parentNode){
36361                        n.parentNode.removeChild(n);
36362                    }
36363                }
36364            }
36365         }
36366     },
36367
36368     /**
36369      * Clears the current filter. Note: with the "remove" option
36370      * set a filter cannot be cleared.
36371      */
36372     clear : function(){
36373         var t = this.tree;
36374         var af = this.filtered;
36375         for(var id in af){
36376             if(typeof id != "function"){
36377                 var n = af[id];
36378                 if(n){
36379                     n.ui.show();
36380                 }
36381             }
36382         }
36383         this.filtered = {};
36384     }
36385 };
36386 /*
36387  * Based on:
36388  * Ext JS Library 1.1.1
36389  * Copyright(c) 2006-2007, Ext JS, LLC.
36390  *
36391  * Originally Released Under LGPL - original licence link has changed is not relivant.
36392  *
36393  * Fork - LGPL
36394  * <script type="text/javascript">
36395  */
36396  
36397
36398 /**
36399  * @class Roo.tree.TreeSorter
36400  * Provides sorting of nodes in a TreePanel
36401  * 
36402  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36403  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36404  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36405  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36406  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36407  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36408  * @constructor
36409  * @param {TreePanel} tree
36410  * @param {Object} config
36411  */
36412 Roo.tree.TreeSorter = function(tree, config){
36413     Roo.apply(this, config);
36414     tree.on("beforechildrenrendered", this.doSort, this);
36415     tree.on("append", this.updateSort, this);
36416     tree.on("insert", this.updateSort, this);
36417     
36418     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36419     var p = this.property || "text";
36420     var sortType = this.sortType;
36421     var fs = this.folderSort;
36422     var cs = this.caseSensitive === true;
36423     var leafAttr = this.leafAttr || 'leaf';
36424
36425     this.sortFn = function(n1, n2){
36426         if(fs){
36427             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36428                 return 1;
36429             }
36430             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36431                 return -1;
36432             }
36433         }
36434         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36435         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36436         if(v1 < v2){
36437                         return dsc ? +1 : -1;
36438                 }else if(v1 > v2){
36439                         return dsc ? -1 : +1;
36440         }else{
36441                 return 0;
36442         }
36443     };
36444 };
36445
36446 Roo.tree.TreeSorter.prototype = {
36447     doSort : function(node){
36448         node.sort(this.sortFn);
36449     },
36450     
36451     compareNodes : function(n1, n2){
36452         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36453     },
36454     
36455     updateSort : function(tree, node){
36456         if(node.childrenRendered){
36457             this.doSort.defer(1, this, [node]);
36458         }
36459     }
36460 };/*
36461  * Based on:
36462  * Ext JS Library 1.1.1
36463  * Copyright(c) 2006-2007, Ext JS, LLC.
36464  *
36465  * Originally Released Under LGPL - original licence link has changed is not relivant.
36466  *
36467  * Fork - LGPL
36468  * <script type="text/javascript">
36469  */
36470
36471 if(Roo.dd.DropZone){
36472     
36473 Roo.tree.TreeDropZone = function(tree, config){
36474     this.allowParentInsert = false;
36475     this.allowContainerDrop = false;
36476     this.appendOnly = false;
36477     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36478     this.tree = tree;
36479     this.lastInsertClass = "x-tree-no-status";
36480     this.dragOverData = {};
36481 };
36482
36483 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36484     ddGroup : "TreeDD",
36485     scroll:  true,
36486     
36487     expandDelay : 1000,
36488     
36489     expandNode : function(node){
36490         if(node.hasChildNodes() && !node.isExpanded()){
36491             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36492         }
36493     },
36494     
36495     queueExpand : function(node){
36496         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36497     },
36498     
36499     cancelExpand : function(){
36500         if(this.expandProcId){
36501             clearTimeout(this.expandProcId);
36502             this.expandProcId = false;
36503         }
36504     },
36505     
36506     isValidDropPoint : function(n, pt, dd, e, data){
36507         if(!n || !data){ return false; }
36508         var targetNode = n.node;
36509         var dropNode = data.node;
36510         // default drop rules
36511         if(!(targetNode && targetNode.isTarget && pt)){
36512             return false;
36513         }
36514         if(pt == "append" && targetNode.allowChildren === false){
36515             return false;
36516         }
36517         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36518             return false;
36519         }
36520         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36521             return false;
36522         }
36523         // reuse the object
36524         var overEvent = this.dragOverData;
36525         overEvent.tree = this.tree;
36526         overEvent.target = targetNode;
36527         overEvent.data = data;
36528         overEvent.point = pt;
36529         overEvent.source = dd;
36530         overEvent.rawEvent = e;
36531         overEvent.dropNode = dropNode;
36532         overEvent.cancel = false;  
36533         var result = this.tree.fireEvent("nodedragover", overEvent);
36534         return overEvent.cancel === false && result !== false;
36535     },
36536     
36537     getDropPoint : function(e, n, dd)
36538     {
36539         var tn = n.node;
36540         if(tn.isRoot){
36541             return tn.allowChildren !== false ? "append" : false; // always append for root
36542         }
36543         var dragEl = n.ddel;
36544         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36545         var y = Roo.lib.Event.getPageY(e);
36546         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36547         
36548         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36549         var noAppend = tn.allowChildren === false;
36550         if(this.appendOnly || tn.parentNode.allowChildren === false){
36551             return noAppend ? false : "append";
36552         }
36553         var noBelow = false;
36554         if(!this.allowParentInsert){
36555             noBelow = tn.hasChildNodes() && tn.isExpanded();
36556         }
36557         var q = (b - t) / (noAppend ? 2 : 3);
36558         if(y >= t && y < (t + q)){
36559             return "above";
36560         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36561             return "below";
36562         }else{
36563             return "append";
36564         }
36565     },
36566     
36567     onNodeEnter : function(n, dd, e, data)
36568     {
36569         this.cancelExpand();
36570     },
36571     
36572     onNodeOver : function(n, dd, e, data)
36573     {
36574        
36575         var pt = this.getDropPoint(e, n, dd);
36576         var node = n.node;
36577         
36578         // auto node expand check
36579         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36580             this.queueExpand(node);
36581         }else if(pt != "append"){
36582             this.cancelExpand();
36583         }
36584         
36585         // set the insert point style on the target node
36586         var returnCls = this.dropNotAllowed;
36587         if(this.isValidDropPoint(n, pt, dd, e, data)){
36588            if(pt){
36589                var el = n.ddel;
36590                var cls;
36591                if(pt == "above"){
36592                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36593                    cls = "x-tree-drag-insert-above";
36594                }else if(pt == "below"){
36595                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36596                    cls = "x-tree-drag-insert-below";
36597                }else{
36598                    returnCls = "x-tree-drop-ok-append";
36599                    cls = "x-tree-drag-append";
36600                }
36601                if(this.lastInsertClass != cls){
36602                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36603                    this.lastInsertClass = cls;
36604                }
36605            }
36606        }
36607        return returnCls;
36608     },
36609     
36610     onNodeOut : function(n, dd, e, data){
36611         
36612         this.cancelExpand();
36613         this.removeDropIndicators(n);
36614     },
36615     
36616     onNodeDrop : function(n, dd, e, data){
36617         var point = this.getDropPoint(e, n, dd);
36618         var targetNode = n.node;
36619         targetNode.ui.startDrop();
36620         if(!this.isValidDropPoint(n, point, dd, e, data)){
36621             targetNode.ui.endDrop();
36622             return false;
36623         }
36624         // first try to find the drop node
36625         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36626         var dropEvent = {
36627             tree : this.tree,
36628             target: targetNode,
36629             data: data,
36630             point: point,
36631             source: dd,
36632             rawEvent: e,
36633             dropNode: dropNode,
36634             cancel: !dropNode   
36635         };
36636         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36637         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36638             targetNode.ui.endDrop();
36639             return false;
36640         }
36641         // allow target changing
36642         targetNode = dropEvent.target;
36643         if(point == "append" && !targetNode.isExpanded()){
36644             targetNode.expand(false, null, function(){
36645                 this.completeDrop(dropEvent);
36646             }.createDelegate(this));
36647         }else{
36648             this.completeDrop(dropEvent);
36649         }
36650         return true;
36651     },
36652     
36653     completeDrop : function(de){
36654         var ns = de.dropNode, p = de.point, t = de.target;
36655         if(!(ns instanceof Array)){
36656             ns = [ns];
36657         }
36658         var n;
36659         for(var i = 0, len = ns.length; i < len; i++){
36660             n = ns[i];
36661             if(p == "above"){
36662                 t.parentNode.insertBefore(n, t);
36663             }else if(p == "below"){
36664                 t.parentNode.insertBefore(n, t.nextSibling);
36665             }else{
36666                 t.appendChild(n);
36667             }
36668         }
36669         n.ui.focus();
36670         if(this.tree.hlDrop){
36671             n.ui.highlight();
36672         }
36673         t.ui.endDrop();
36674         this.tree.fireEvent("nodedrop", de);
36675     },
36676     
36677     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36678         if(this.tree.hlDrop){
36679             dropNode.ui.focus();
36680             dropNode.ui.highlight();
36681         }
36682         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36683     },
36684     
36685     getTree : function(){
36686         return this.tree;
36687     },
36688     
36689     removeDropIndicators : function(n){
36690         if(n && n.ddel){
36691             var el = n.ddel;
36692             Roo.fly(el).removeClass([
36693                     "x-tree-drag-insert-above",
36694                     "x-tree-drag-insert-below",
36695                     "x-tree-drag-append"]);
36696             this.lastInsertClass = "_noclass";
36697         }
36698     },
36699     
36700     beforeDragDrop : function(target, e, id){
36701         this.cancelExpand();
36702         return true;
36703     },
36704     
36705     afterRepair : function(data){
36706         if(data && Roo.enableFx){
36707             data.node.ui.highlight();
36708         }
36709         this.hideProxy();
36710     } 
36711     
36712 });
36713
36714 }
36715 /*
36716  * Based on:
36717  * Ext JS Library 1.1.1
36718  * Copyright(c) 2006-2007, Ext JS, LLC.
36719  *
36720  * Originally Released Under LGPL - original licence link has changed is not relivant.
36721  *
36722  * Fork - LGPL
36723  * <script type="text/javascript">
36724  */
36725  
36726
36727 if(Roo.dd.DragZone){
36728 Roo.tree.TreeDragZone = function(tree, config){
36729     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36730     this.tree = tree;
36731 };
36732
36733 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36734     ddGroup : "TreeDD",
36735    
36736     onBeforeDrag : function(data, e){
36737         var n = data.node;
36738         return n && n.draggable && !n.disabled;
36739     },
36740      
36741     
36742     onInitDrag : function(e){
36743         var data = this.dragData;
36744         this.tree.getSelectionModel().select(data.node);
36745         this.proxy.update("");
36746         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36747         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36748     },
36749     
36750     getRepairXY : function(e, data){
36751         return data.node.ui.getDDRepairXY();
36752     },
36753     
36754     onEndDrag : function(data, e){
36755         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36756         
36757         
36758     },
36759     
36760     onValidDrop : function(dd, e, id){
36761         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36762         this.hideProxy();
36763     },
36764     
36765     beforeInvalidDrop : function(e, id){
36766         // this scrolls the original position back into view
36767         var sm = this.tree.getSelectionModel();
36768         sm.clearSelections();
36769         sm.select(this.dragData.node);
36770     }
36771 });
36772 }/*
36773  * Based on:
36774  * Ext JS Library 1.1.1
36775  * Copyright(c) 2006-2007, Ext JS, LLC.
36776  *
36777  * Originally Released Under LGPL - original licence link has changed is not relivant.
36778  *
36779  * Fork - LGPL
36780  * <script type="text/javascript">
36781  */
36782 /**
36783  * @class Roo.tree.TreeEditor
36784  * @extends Roo.Editor
36785  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36786  * as the editor field.
36787  * @constructor
36788  * @param {Object} config (used to be the tree panel.)
36789  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36790  * 
36791  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36792  * @cfg {Roo.form.TextField|Object} field The field configuration
36793  *
36794  * 
36795  */
36796 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36797     var tree = config;
36798     var field;
36799     if (oldconfig) { // old style..
36800         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36801     } else {
36802         // new style..
36803         tree = config.tree;
36804         config.field = config.field  || {};
36805         config.field.xtype = 'TextField';
36806         field = Roo.factory(config.field, Roo.form);
36807     }
36808     config = config || {};
36809     
36810     
36811     this.addEvents({
36812         /**
36813          * @event beforenodeedit
36814          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36815          * false from the handler of this event.
36816          * @param {Editor} this
36817          * @param {Roo.tree.Node} node 
36818          */
36819         "beforenodeedit" : true
36820     });
36821     
36822     //Roo.log(config);
36823     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36824
36825     this.tree = tree;
36826
36827     tree.on('beforeclick', this.beforeNodeClick, this);
36828     tree.getTreeEl().on('mousedown', this.hide, this);
36829     this.on('complete', this.updateNode, this);
36830     this.on('beforestartedit', this.fitToTree, this);
36831     this.on('startedit', this.bindScroll, this, {delay:10});
36832     this.on('specialkey', this.onSpecialKey, this);
36833 };
36834
36835 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36836     /**
36837      * @cfg {String} alignment
36838      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36839      */
36840     alignment: "l-l",
36841     // inherit
36842     autoSize: false,
36843     /**
36844      * @cfg {Boolean} hideEl
36845      * True to hide the bound element while the editor is displayed (defaults to false)
36846      */
36847     hideEl : false,
36848     /**
36849      * @cfg {String} cls
36850      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36851      */
36852     cls: "x-small-editor x-tree-editor",
36853     /**
36854      * @cfg {Boolean} shim
36855      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36856      */
36857     shim:false,
36858     // inherit
36859     shadow:"frame",
36860     /**
36861      * @cfg {Number} maxWidth
36862      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36863      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36864      * scroll and client offsets into account prior to each edit.
36865      */
36866     maxWidth: 250,
36867
36868     editDelay : 350,
36869
36870     // private
36871     fitToTree : function(ed, el){
36872         var td = this.tree.getTreeEl().dom, nd = el.dom;
36873         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36874             td.scrollLeft = nd.offsetLeft;
36875         }
36876         var w = Math.min(
36877                 this.maxWidth,
36878                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36879         this.setSize(w, '');
36880         
36881         return this.fireEvent('beforenodeedit', this, this.editNode);
36882         
36883     },
36884
36885     // private
36886     triggerEdit : function(node){
36887         this.completeEdit();
36888         this.editNode = node;
36889         this.startEdit(node.ui.textNode, node.text);
36890     },
36891
36892     // private
36893     bindScroll : function(){
36894         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36895     },
36896
36897     // private
36898     beforeNodeClick : function(node, e){
36899         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36900         this.lastClick = new Date();
36901         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36902             e.stopEvent();
36903             this.triggerEdit(node);
36904             return false;
36905         }
36906         return true;
36907     },
36908
36909     // private
36910     updateNode : function(ed, value){
36911         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36912         this.editNode.setText(value);
36913     },
36914
36915     // private
36916     onHide : function(){
36917         Roo.tree.TreeEditor.superclass.onHide.call(this);
36918         if(this.editNode){
36919             this.editNode.ui.focus();
36920         }
36921     },
36922
36923     // private
36924     onSpecialKey : function(field, e){
36925         var k = e.getKey();
36926         if(k == e.ESC){
36927             e.stopEvent();
36928             this.cancelEdit();
36929         }else if(k == e.ENTER && !e.hasModifier()){
36930             e.stopEvent();
36931             this.completeEdit();
36932         }
36933     }
36934 });//<Script type="text/javascript">
36935 /*
36936  * Based on:
36937  * Ext JS Library 1.1.1
36938  * Copyright(c) 2006-2007, Ext JS, LLC.
36939  *
36940  * Originally Released Under LGPL - original licence link has changed is not relivant.
36941  *
36942  * Fork - LGPL
36943  * <script type="text/javascript">
36944  */
36945  
36946 /**
36947  * Not documented??? - probably should be...
36948  */
36949
36950 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36951     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36952     
36953     renderElements : function(n, a, targetNode, bulkRender){
36954         //consel.log("renderElements?");
36955         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36956
36957         var t = n.getOwnerTree();
36958         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36959         
36960         var cols = t.columns;
36961         var bw = t.borderWidth;
36962         var c = cols[0];
36963         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36964          var cb = typeof a.checked == "boolean";
36965         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36966         var colcls = 'x-t-' + tid + '-c0';
36967         var buf = [
36968             '<li class="x-tree-node">',
36969             
36970                 
36971                 '<div class="x-tree-node-el ', a.cls,'">',
36972                     // extran...
36973                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36974                 
36975                 
36976                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36977                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36978                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36979                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36980                            (a.iconCls ? ' '+a.iconCls : ''),
36981                            '" unselectable="on" />',
36982                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36983                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36984                              
36985                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36986                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36987                             '<span unselectable="on" qtip="' + tx + '">',
36988                              tx,
36989                              '</span></a>' ,
36990                     '</div>',
36991                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36992                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36993                  ];
36994         for(var i = 1, len = cols.length; i < len; i++){
36995             c = cols[i];
36996             colcls = 'x-t-' + tid + '-c' +i;
36997             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36998             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36999                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37000                       "</div>");
37001          }
37002          
37003          buf.push(
37004             '</a>',
37005             '<div class="x-clear"></div></div>',
37006             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37007             "</li>");
37008         
37009         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37010             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37011                                 n.nextSibling.ui.getEl(), buf.join(""));
37012         }else{
37013             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37014         }
37015         var el = this.wrap.firstChild;
37016         this.elRow = el;
37017         this.elNode = el.firstChild;
37018         this.ranchor = el.childNodes[1];
37019         this.ctNode = this.wrap.childNodes[1];
37020         var cs = el.firstChild.childNodes;
37021         this.indentNode = cs[0];
37022         this.ecNode = cs[1];
37023         this.iconNode = cs[2];
37024         var index = 3;
37025         if(cb){
37026             this.checkbox = cs[3];
37027             index++;
37028         }
37029         this.anchor = cs[index];
37030         
37031         this.textNode = cs[index].firstChild;
37032         
37033         //el.on("click", this.onClick, this);
37034         //el.on("dblclick", this.onDblClick, this);
37035         
37036         
37037        // console.log(this);
37038     },
37039     initEvents : function(){
37040         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37041         
37042             
37043         var a = this.ranchor;
37044
37045         var el = Roo.get(a);
37046
37047         if(Roo.isOpera){ // opera render bug ignores the CSS
37048             el.setStyle("text-decoration", "none");
37049         }
37050
37051         el.on("click", this.onClick, this);
37052         el.on("dblclick", this.onDblClick, this);
37053         el.on("contextmenu", this.onContextMenu, this);
37054         
37055     },
37056     
37057     /*onSelectedChange : function(state){
37058         if(state){
37059             this.focus();
37060             this.addClass("x-tree-selected");
37061         }else{
37062             //this.blur();
37063             this.removeClass("x-tree-selected");
37064         }
37065     },*/
37066     addClass : function(cls){
37067         if(this.elRow){
37068             Roo.fly(this.elRow).addClass(cls);
37069         }
37070         
37071     },
37072     
37073     
37074     removeClass : function(cls){
37075         if(this.elRow){
37076             Roo.fly(this.elRow).removeClass(cls);
37077         }
37078     }
37079
37080     
37081     
37082 });//<Script type="text/javascript">
37083
37084 /*
37085  * Based on:
37086  * Ext JS Library 1.1.1
37087  * Copyright(c) 2006-2007, Ext JS, LLC.
37088  *
37089  * Originally Released Under LGPL - original licence link has changed is not relivant.
37090  *
37091  * Fork - LGPL
37092  * <script type="text/javascript">
37093  */
37094  
37095
37096 /**
37097  * @class Roo.tree.ColumnTree
37098  * @extends Roo.data.TreePanel
37099  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37100  * @cfg {int} borderWidth  compined right/left border allowance
37101  * @constructor
37102  * @param {String/HTMLElement/Element} el The container element
37103  * @param {Object} config
37104  */
37105 Roo.tree.ColumnTree =  function(el, config)
37106 {
37107    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37108    this.addEvents({
37109         /**
37110         * @event resize
37111         * Fire this event on a container when it resizes
37112         * @param {int} w Width
37113         * @param {int} h Height
37114         */
37115        "resize" : true
37116     });
37117     this.on('resize', this.onResize, this);
37118 };
37119
37120 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37121     //lines:false,
37122     
37123     
37124     borderWidth: Roo.isBorderBox ? 0 : 2, 
37125     headEls : false,
37126     
37127     render : function(){
37128         // add the header.....
37129        
37130         Roo.tree.ColumnTree.superclass.render.apply(this);
37131         
37132         this.el.addClass('x-column-tree');
37133         
37134         this.headers = this.el.createChild(
37135             {cls:'x-tree-headers'},this.innerCt.dom);
37136    
37137         var cols = this.columns, c;
37138         var totalWidth = 0;
37139         this.headEls = [];
37140         var  len = cols.length;
37141         for(var i = 0; i < len; i++){
37142              c = cols[i];
37143              totalWidth += c.width;
37144             this.headEls.push(this.headers.createChild({
37145                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37146                  cn: {
37147                      cls:'x-tree-hd-text',
37148                      html: c.header
37149                  },
37150                  style:'width:'+(c.width-this.borderWidth)+'px;'
37151              }));
37152         }
37153         this.headers.createChild({cls:'x-clear'});
37154         // prevent floats from wrapping when clipped
37155         this.headers.setWidth(totalWidth);
37156         //this.innerCt.setWidth(totalWidth);
37157         this.innerCt.setStyle({ overflow: 'auto' });
37158         this.onResize(this.width, this.height);
37159              
37160         
37161     },
37162     onResize : function(w,h)
37163     {
37164         this.height = h;
37165         this.width = w;
37166         // resize cols..
37167         this.innerCt.setWidth(this.width);
37168         this.innerCt.setHeight(this.height-20);
37169         
37170         // headers...
37171         var cols = this.columns, c;
37172         var totalWidth = 0;
37173         var expEl = false;
37174         var len = cols.length;
37175         for(var i = 0; i < len; i++){
37176             c = cols[i];
37177             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37178                 // it's the expander..
37179                 expEl  = this.headEls[i];
37180                 continue;
37181             }
37182             totalWidth += c.width;
37183             
37184         }
37185         if (expEl) {
37186             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37187         }
37188         this.headers.setWidth(w-20);
37189
37190         
37191         
37192         
37193     }
37194 });
37195 /*
37196  * Based on:
37197  * Ext JS Library 1.1.1
37198  * Copyright(c) 2006-2007, Ext JS, LLC.
37199  *
37200  * Originally Released Under LGPL - original licence link has changed is not relivant.
37201  *
37202  * Fork - LGPL
37203  * <script type="text/javascript">
37204  */
37205  
37206 /**
37207  * @class Roo.menu.Menu
37208  * @extends Roo.util.Observable
37209  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37210  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37211  * @constructor
37212  * Creates a new Menu
37213  * @param {Object} config Configuration options
37214  */
37215 Roo.menu.Menu = function(config){
37216     Roo.apply(this, config);
37217     this.id = this.id || Roo.id();
37218     this.addEvents({
37219         /**
37220          * @event beforeshow
37221          * Fires before this menu is displayed
37222          * @param {Roo.menu.Menu} this
37223          */
37224         beforeshow : true,
37225         /**
37226          * @event beforehide
37227          * Fires before this menu is hidden
37228          * @param {Roo.menu.Menu} this
37229          */
37230         beforehide : true,
37231         /**
37232          * @event show
37233          * Fires after this menu is displayed
37234          * @param {Roo.menu.Menu} this
37235          */
37236         show : true,
37237         /**
37238          * @event hide
37239          * Fires after this menu is hidden
37240          * @param {Roo.menu.Menu} this
37241          */
37242         hide : true,
37243         /**
37244          * @event click
37245          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37246          * @param {Roo.menu.Menu} this
37247          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37248          * @param {Roo.EventObject} e
37249          */
37250         click : true,
37251         /**
37252          * @event mouseover
37253          * Fires when the mouse is hovering over this menu
37254          * @param {Roo.menu.Menu} this
37255          * @param {Roo.EventObject} e
37256          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37257          */
37258         mouseover : true,
37259         /**
37260          * @event mouseout
37261          * Fires when the mouse exits this menu
37262          * @param {Roo.menu.Menu} this
37263          * @param {Roo.EventObject} e
37264          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37265          */
37266         mouseout : true,
37267         /**
37268          * @event itemclick
37269          * Fires when a menu item contained in this menu is clicked
37270          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37271          * @param {Roo.EventObject} e
37272          */
37273         itemclick: true
37274     });
37275     if (this.registerMenu) {
37276         Roo.menu.MenuMgr.register(this);
37277     }
37278     
37279     var mis = this.items;
37280     this.items = new Roo.util.MixedCollection();
37281     if(mis){
37282         this.add.apply(this, mis);
37283     }
37284 };
37285
37286 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37287     /**
37288      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37289      */
37290     minWidth : 120,
37291     /**
37292      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37293      * for bottom-right shadow (defaults to "sides")
37294      */
37295     shadow : "sides",
37296     /**
37297      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37298      * this menu (defaults to "tl-tr?")
37299      */
37300     subMenuAlign : "tl-tr?",
37301     /**
37302      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37303      * relative to its element of origin (defaults to "tl-bl?")
37304      */
37305     defaultAlign : "tl-bl?",
37306     /**
37307      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37308      */
37309     allowOtherMenus : false,
37310     /**
37311      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37312      */
37313     registerMenu : true,
37314
37315     hidden:true,
37316
37317     // private
37318     render : function(){
37319         if(this.el){
37320             return;
37321         }
37322         var el = this.el = new Roo.Layer({
37323             cls: "x-menu",
37324             shadow:this.shadow,
37325             constrain: false,
37326             parentEl: this.parentEl || document.body,
37327             zindex:15000
37328         });
37329
37330         this.keyNav = new Roo.menu.MenuNav(this);
37331
37332         if(this.plain){
37333             el.addClass("x-menu-plain");
37334         }
37335         if(this.cls){
37336             el.addClass(this.cls);
37337         }
37338         // generic focus element
37339         this.focusEl = el.createChild({
37340             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37341         });
37342         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37343         //disabling touch- as it's causing issues ..
37344         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37345         ul.on('click'   , this.onClick, this);
37346         
37347         
37348         ul.on("mouseover", this.onMouseOver, this);
37349         ul.on("mouseout", this.onMouseOut, this);
37350         this.items.each(function(item){
37351             if (item.hidden) {
37352                 return;
37353             }
37354             
37355             var li = document.createElement("li");
37356             li.className = "x-menu-list-item";
37357             ul.dom.appendChild(li);
37358             item.render(li, this);
37359         }, this);
37360         this.ul = ul;
37361         this.autoWidth();
37362     },
37363
37364     // private
37365     autoWidth : function(){
37366         var el = this.el, ul = this.ul;
37367         if(!el){
37368             return;
37369         }
37370         var w = this.width;
37371         if(w){
37372             el.setWidth(w);
37373         }else if(Roo.isIE){
37374             el.setWidth(this.minWidth);
37375             var t = el.dom.offsetWidth; // force recalc
37376             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37377         }
37378     },
37379
37380     // private
37381     delayAutoWidth : function(){
37382         if(this.rendered){
37383             if(!this.awTask){
37384                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37385             }
37386             this.awTask.delay(20);
37387         }
37388     },
37389
37390     // private
37391     findTargetItem : function(e){
37392         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37393         if(t && t.menuItemId){
37394             return this.items.get(t.menuItemId);
37395         }
37396     },
37397
37398     // private
37399     onClick : function(e){
37400         Roo.log("menu.onClick");
37401         var t = this.findTargetItem(e);
37402         if(!t){
37403             return;
37404         }
37405         Roo.log(e);
37406         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37407             if(t == this.activeItem && t.shouldDeactivate(e)){
37408                 this.activeItem.deactivate();
37409                 delete this.activeItem;
37410                 return;
37411             }
37412             if(t.canActivate){
37413                 this.setActiveItem(t, true);
37414             }
37415             return;
37416             
37417             
37418         }
37419         
37420         t.onClick(e);
37421         this.fireEvent("click", this, t, e);
37422     },
37423
37424     // private
37425     setActiveItem : function(item, autoExpand){
37426         if(item != this.activeItem){
37427             if(this.activeItem){
37428                 this.activeItem.deactivate();
37429             }
37430             this.activeItem = item;
37431             item.activate(autoExpand);
37432         }else if(autoExpand){
37433             item.expandMenu();
37434         }
37435     },
37436
37437     // private
37438     tryActivate : function(start, step){
37439         var items = this.items;
37440         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37441             var item = items.get(i);
37442             if(!item.disabled && item.canActivate){
37443                 this.setActiveItem(item, false);
37444                 return item;
37445             }
37446         }
37447         return false;
37448     },
37449
37450     // private
37451     onMouseOver : function(e){
37452         var t;
37453         if(t = this.findTargetItem(e)){
37454             if(t.canActivate && !t.disabled){
37455                 this.setActiveItem(t, true);
37456             }
37457         }
37458         this.fireEvent("mouseover", this, e, t);
37459     },
37460
37461     // private
37462     onMouseOut : function(e){
37463         var t;
37464         if(t = this.findTargetItem(e)){
37465             if(t == this.activeItem && t.shouldDeactivate(e)){
37466                 this.activeItem.deactivate();
37467                 delete this.activeItem;
37468             }
37469         }
37470         this.fireEvent("mouseout", this, e, t);
37471     },
37472
37473     /**
37474      * Read-only.  Returns true if the menu is currently displayed, else false.
37475      * @type Boolean
37476      */
37477     isVisible : function(){
37478         return this.el && !this.hidden;
37479     },
37480
37481     /**
37482      * Displays this menu relative to another element
37483      * @param {String/HTMLElement/Roo.Element} element The element to align to
37484      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37485      * the element (defaults to this.defaultAlign)
37486      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37487      */
37488     show : function(el, pos, parentMenu){
37489         this.parentMenu = parentMenu;
37490         if(!this.el){
37491             this.render();
37492         }
37493         this.fireEvent("beforeshow", this);
37494         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37495     },
37496
37497     /**
37498      * Displays this menu at a specific xy position
37499      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37500      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37501      */
37502     showAt : function(xy, parentMenu, /* private: */_e){
37503         this.parentMenu = parentMenu;
37504         if(!this.el){
37505             this.render();
37506         }
37507         if(_e !== false){
37508             this.fireEvent("beforeshow", this);
37509             xy = this.el.adjustForConstraints(xy);
37510         }
37511         this.el.setXY(xy);
37512         this.el.show();
37513         this.hidden = false;
37514         this.focus();
37515         this.fireEvent("show", this);
37516     },
37517
37518     focus : function(){
37519         if(!this.hidden){
37520             this.doFocus.defer(50, this);
37521         }
37522     },
37523
37524     doFocus : function(){
37525         if(!this.hidden){
37526             this.focusEl.focus();
37527         }
37528     },
37529
37530     /**
37531      * Hides this menu and optionally all parent menus
37532      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37533      */
37534     hide : function(deep){
37535         if(this.el && this.isVisible()){
37536             this.fireEvent("beforehide", this);
37537             if(this.activeItem){
37538                 this.activeItem.deactivate();
37539                 this.activeItem = null;
37540             }
37541             this.el.hide();
37542             this.hidden = true;
37543             this.fireEvent("hide", this);
37544         }
37545         if(deep === true && this.parentMenu){
37546             this.parentMenu.hide(true);
37547         }
37548     },
37549
37550     /**
37551      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37552      * Any of the following are valid:
37553      * <ul>
37554      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37555      * <li>An HTMLElement object which will be converted to a menu item</li>
37556      * <li>A menu item config object that will be created as a new menu item</li>
37557      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37558      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37559      * </ul>
37560      * Usage:
37561      * <pre><code>
37562 // Create the menu
37563 var menu = new Roo.menu.Menu();
37564
37565 // Create a menu item to add by reference
37566 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37567
37568 // Add a bunch of items at once using different methods.
37569 // Only the last item added will be returned.
37570 var item = menu.add(
37571     menuItem,                // add existing item by ref
37572     'Dynamic Item',          // new TextItem
37573     '-',                     // new separator
37574     { text: 'Config Item' }  // new item by config
37575 );
37576 </code></pre>
37577      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37578      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37579      */
37580     add : function(){
37581         var a = arguments, l = a.length, item;
37582         for(var i = 0; i < l; i++){
37583             var el = a[i];
37584             if ((typeof(el) == "object") && el.xtype && el.xns) {
37585                 el = Roo.factory(el, Roo.menu);
37586             }
37587             
37588             if(el.render){ // some kind of Item
37589                 item = this.addItem(el);
37590             }else if(typeof el == "string"){ // string
37591                 if(el == "separator" || el == "-"){
37592                     item = this.addSeparator();
37593                 }else{
37594                     item = this.addText(el);
37595                 }
37596             }else if(el.tagName || el.el){ // element
37597                 item = this.addElement(el);
37598             }else if(typeof el == "object"){ // must be menu item config?
37599                 item = this.addMenuItem(el);
37600             }
37601         }
37602         return item;
37603     },
37604
37605     /**
37606      * Returns this menu's underlying {@link Roo.Element} object
37607      * @return {Roo.Element} The element
37608      */
37609     getEl : function(){
37610         if(!this.el){
37611             this.render();
37612         }
37613         return this.el;
37614     },
37615
37616     /**
37617      * Adds a separator bar to the menu
37618      * @return {Roo.menu.Item} The menu item that was added
37619      */
37620     addSeparator : function(){
37621         return this.addItem(new Roo.menu.Separator());
37622     },
37623
37624     /**
37625      * Adds an {@link Roo.Element} object to the menu
37626      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37627      * @return {Roo.menu.Item} The menu item that was added
37628      */
37629     addElement : function(el){
37630         return this.addItem(new Roo.menu.BaseItem(el));
37631     },
37632
37633     /**
37634      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37635      * @param {Roo.menu.Item} item The menu item to add
37636      * @return {Roo.menu.Item} The menu item that was added
37637      */
37638     addItem : function(item){
37639         this.items.add(item);
37640         if(this.ul){
37641             var li = document.createElement("li");
37642             li.className = "x-menu-list-item";
37643             this.ul.dom.appendChild(li);
37644             item.render(li, this);
37645             this.delayAutoWidth();
37646         }
37647         return item;
37648     },
37649
37650     /**
37651      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37652      * @param {Object} config A MenuItem config object
37653      * @return {Roo.menu.Item} The menu item that was added
37654      */
37655     addMenuItem : function(config){
37656         if(!(config instanceof Roo.menu.Item)){
37657             if(typeof config.checked == "boolean"){ // must be check menu item config?
37658                 config = new Roo.menu.CheckItem(config);
37659             }else{
37660                 config = new Roo.menu.Item(config);
37661             }
37662         }
37663         return this.addItem(config);
37664     },
37665
37666     /**
37667      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37668      * @param {String} text The text to display in the menu item
37669      * @return {Roo.menu.Item} The menu item that was added
37670      */
37671     addText : function(text){
37672         return this.addItem(new Roo.menu.TextItem({ text : text }));
37673     },
37674
37675     /**
37676      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37677      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37678      * @param {Roo.menu.Item} item The menu item to add
37679      * @return {Roo.menu.Item} The menu item that was added
37680      */
37681     insert : function(index, item){
37682         this.items.insert(index, item);
37683         if(this.ul){
37684             var li = document.createElement("li");
37685             li.className = "x-menu-list-item";
37686             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37687             item.render(li, this);
37688             this.delayAutoWidth();
37689         }
37690         return item;
37691     },
37692
37693     /**
37694      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37695      * @param {Roo.menu.Item} item The menu item to remove
37696      */
37697     remove : function(item){
37698         this.items.removeKey(item.id);
37699         item.destroy();
37700     },
37701
37702     /**
37703      * Removes and destroys all items in the menu
37704      */
37705     removeAll : function(){
37706         var f;
37707         while(f = this.items.first()){
37708             this.remove(f);
37709         }
37710     }
37711 });
37712
37713 // MenuNav is a private utility class used internally by the Menu
37714 Roo.menu.MenuNav = function(menu){
37715     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37716     this.scope = this.menu = menu;
37717 };
37718
37719 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37720     doRelay : function(e, h){
37721         var k = e.getKey();
37722         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37723             this.menu.tryActivate(0, 1);
37724             return false;
37725         }
37726         return h.call(this.scope || this, e, this.menu);
37727     },
37728
37729     up : function(e, m){
37730         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37731             m.tryActivate(m.items.length-1, -1);
37732         }
37733     },
37734
37735     down : function(e, m){
37736         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37737             m.tryActivate(0, 1);
37738         }
37739     },
37740
37741     right : function(e, m){
37742         if(m.activeItem){
37743             m.activeItem.expandMenu(true);
37744         }
37745     },
37746
37747     left : function(e, m){
37748         m.hide();
37749         if(m.parentMenu && m.parentMenu.activeItem){
37750             m.parentMenu.activeItem.activate();
37751         }
37752     },
37753
37754     enter : function(e, m){
37755         if(m.activeItem){
37756             e.stopPropagation();
37757             m.activeItem.onClick(e);
37758             m.fireEvent("click", this, m.activeItem);
37759             return true;
37760         }
37761     }
37762 });/*
37763  * Based on:
37764  * Ext JS Library 1.1.1
37765  * Copyright(c) 2006-2007, Ext JS, LLC.
37766  *
37767  * Originally Released Under LGPL - original licence link has changed is not relivant.
37768  *
37769  * Fork - LGPL
37770  * <script type="text/javascript">
37771  */
37772  
37773 /**
37774  * @class Roo.menu.MenuMgr
37775  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37776  * @singleton
37777  */
37778 Roo.menu.MenuMgr = function(){
37779    var menus, active, groups = {}, attached = false, lastShow = new Date();
37780
37781    // private - called when first menu is created
37782    function init(){
37783        menus = {};
37784        active = new Roo.util.MixedCollection();
37785        Roo.get(document).addKeyListener(27, function(){
37786            if(active.length > 0){
37787                hideAll();
37788            }
37789        });
37790    }
37791
37792    // private
37793    function hideAll(){
37794        if(active && active.length > 0){
37795            var c = active.clone();
37796            c.each(function(m){
37797                m.hide();
37798            });
37799        }
37800    }
37801
37802    // private
37803    function onHide(m){
37804        active.remove(m);
37805        if(active.length < 1){
37806            Roo.get(document).un("mousedown", onMouseDown);
37807            attached = false;
37808        }
37809    }
37810
37811    // private
37812    function onShow(m){
37813        var last = active.last();
37814        lastShow = new Date();
37815        active.add(m);
37816        if(!attached){
37817            Roo.get(document).on("mousedown", onMouseDown);
37818            attached = true;
37819        }
37820        if(m.parentMenu){
37821           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37822           m.parentMenu.activeChild = m;
37823        }else if(last && last.isVisible()){
37824           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37825        }
37826    }
37827
37828    // private
37829    function onBeforeHide(m){
37830        if(m.activeChild){
37831            m.activeChild.hide();
37832        }
37833        if(m.autoHideTimer){
37834            clearTimeout(m.autoHideTimer);
37835            delete m.autoHideTimer;
37836        }
37837    }
37838
37839    // private
37840    function onBeforeShow(m){
37841        var pm = m.parentMenu;
37842        if(!pm && !m.allowOtherMenus){
37843            hideAll();
37844        }else if(pm && pm.activeChild && active != m){
37845            pm.activeChild.hide();
37846        }
37847    }
37848
37849    // private
37850    function onMouseDown(e){
37851        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37852            hideAll();
37853        }
37854    }
37855
37856    // private
37857    function onBeforeCheck(mi, state){
37858        if(state){
37859            var g = groups[mi.group];
37860            for(var i = 0, l = g.length; i < l; i++){
37861                if(g[i] != mi){
37862                    g[i].setChecked(false);
37863                }
37864            }
37865        }
37866    }
37867
37868    return {
37869
37870        /**
37871         * Hides all menus that are currently visible
37872         */
37873        hideAll : function(){
37874             hideAll();  
37875        },
37876
37877        // private
37878        register : function(menu){
37879            if(!menus){
37880                init();
37881            }
37882            menus[menu.id] = menu;
37883            menu.on("beforehide", onBeforeHide);
37884            menu.on("hide", onHide);
37885            menu.on("beforeshow", onBeforeShow);
37886            menu.on("show", onShow);
37887            var g = menu.group;
37888            if(g && menu.events["checkchange"]){
37889                if(!groups[g]){
37890                    groups[g] = [];
37891                }
37892                groups[g].push(menu);
37893                menu.on("checkchange", onCheck);
37894            }
37895        },
37896
37897         /**
37898          * Returns a {@link Roo.menu.Menu} object
37899          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37900          * be used to generate and return a new Menu instance.
37901          */
37902        get : function(menu){
37903            if(typeof menu == "string"){ // menu id
37904                return menus[menu];
37905            }else if(menu.events){  // menu instance
37906                return menu;
37907            }else if(typeof menu.length == 'number'){ // array of menu items?
37908                return new Roo.menu.Menu({items:menu});
37909            }else{ // otherwise, must be a config
37910                return new Roo.menu.Menu(menu);
37911            }
37912        },
37913
37914        // private
37915        unregister : function(menu){
37916            delete menus[menu.id];
37917            menu.un("beforehide", onBeforeHide);
37918            menu.un("hide", onHide);
37919            menu.un("beforeshow", onBeforeShow);
37920            menu.un("show", onShow);
37921            var g = menu.group;
37922            if(g && menu.events["checkchange"]){
37923                groups[g].remove(menu);
37924                menu.un("checkchange", onCheck);
37925            }
37926        },
37927
37928        // private
37929        registerCheckable : function(menuItem){
37930            var g = menuItem.group;
37931            if(g){
37932                if(!groups[g]){
37933                    groups[g] = [];
37934                }
37935                groups[g].push(menuItem);
37936                menuItem.on("beforecheckchange", onBeforeCheck);
37937            }
37938        },
37939
37940        // private
37941        unregisterCheckable : function(menuItem){
37942            var g = menuItem.group;
37943            if(g){
37944                groups[g].remove(menuItem);
37945                menuItem.un("beforecheckchange", onBeforeCheck);
37946            }
37947        }
37948    };
37949 }();/*
37950  * Based on:
37951  * Ext JS Library 1.1.1
37952  * Copyright(c) 2006-2007, Ext JS, LLC.
37953  *
37954  * Originally Released Under LGPL - original licence link has changed is not relivant.
37955  *
37956  * Fork - LGPL
37957  * <script type="text/javascript">
37958  */
37959  
37960
37961 /**
37962  * @class Roo.menu.BaseItem
37963  * @extends Roo.Component
37964  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37965  * management and base configuration options shared by all menu components.
37966  * @constructor
37967  * Creates a new BaseItem
37968  * @param {Object} config Configuration options
37969  */
37970 Roo.menu.BaseItem = function(config){
37971     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37972
37973     this.addEvents({
37974         /**
37975          * @event click
37976          * Fires when this item is clicked
37977          * @param {Roo.menu.BaseItem} this
37978          * @param {Roo.EventObject} e
37979          */
37980         click: true,
37981         /**
37982          * @event activate
37983          * Fires when this item is activated
37984          * @param {Roo.menu.BaseItem} this
37985          */
37986         activate : true,
37987         /**
37988          * @event deactivate
37989          * Fires when this item is deactivated
37990          * @param {Roo.menu.BaseItem} this
37991          */
37992         deactivate : true
37993     });
37994
37995     if(this.handler){
37996         this.on("click", this.handler, this.scope, true);
37997     }
37998 };
37999
38000 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38001     /**
38002      * @cfg {Function} handler
38003      * A function that will handle the click event of this menu item (defaults to undefined)
38004      */
38005     /**
38006      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38007      */
38008     canActivate : false,
38009     
38010      /**
38011      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38012      */
38013     hidden: false,
38014     
38015     /**
38016      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38017      */
38018     activeClass : "x-menu-item-active",
38019     /**
38020      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38021      */
38022     hideOnClick : true,
38023     /**
38024      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38025      */
38026     hideDelay : 100,
38027
38028     // private
38029     ctype: "Roo.menu.BaseItem",
38030
38031     // private
38032     actionMode : "container",
38033
38034     // private
38035     render : function(container, parentMenu){
38036         this.parentMenu = parentMenu;
38037         Roo.menu.BaseItem.superclass.render.call(this, container);
38038         this.container.menuItemId = this.id;
38039     },
38040
38041     // private
38042     onRender : function(container, position){
38043         this.el = Roo.get(this.el);
38044         container.dom.appendChild(this.el.dom);
38045     },
38046
38047     // private
38048     onClick : function(e){
38049         if(!this.disabled && this.fireEvent("click", this, e) !== false
38050                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38051             this.handleClick(e);
38052         }else{
38053             e.stopEvent();
38054         }
38055     },
38056
38057     // private
38058     activate : function(){
38059         if(this.disabled){
38060             return false;
38061         }
38062         var li = this.container;
38063         li.addClass(this.activeClass);
38064         this.region = li.getRegion().adjust(2, 2, -2, -2);
38065         this.fireEvent("activate", this);
38066         return true;
38067     },
38068
38069     // private
38070     deactivate : function(){
38071         this.container.removeClass(this.activeClass);
38072         this.fireEvent("deactivate", this);
38073     },
38074
38075     // private
38076     shouldDeactivate : function(e){
38077         return !this.region || !this.region.contains(e.getPoint());
38078     },
38079
38080     // private
38081     handleClick : function(e){
38082         if(this.hideOnClick){
38083             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38084         }
38085     },
38086
38087     // private
38088     expandMenu : function(autoActivate){
38089         // do nothing
38090     },
38091
38092     // private
38093     hideMenu : function(){
38094         // do nothing
38095     }
38096 });/*
38097  * Based on:
38098  * Ext JS Library 1.1.1
38099  * Copyright(c) 2006-2007, Ext JS, LLC.
38100  *
38101  * Originally Released Under LGPL - original licence link has changed is not relivant.
38102  *
38103  * Fork - LGPL
38104  * <script type="text/javascript">
38105  */
38106  
38107 /**
38108  * @class Roo.menu.Adapter
38109  * @extends Roo.menu.BaseItem
38110  * 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.
38111  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38112  * @constructor
38113  * Creates a new Adapter
38114  * @param {Object} config Configuration options
38115  */
38116 Roo.menu.Adapter = function(component, config){
38117     Roo.menu.Adapter.superclass.constructor.call(this, config);
38118     this.component = component;
38119 };
38120 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38121     // private
38122     canActivate : true,
38123
38124     // private
38125     onRender : function(container, position){
38126         this.component.render(container);
38127         this.el = this.component.getEl();
38128     },
38129
38130     // private
38131     activate : function(){
38132         if(this.disabled){
38133             return false;
38134         }
38135         this.component.focus();
38136         this.fireEvent("activate", this);
38137         return true;
38138     },
38139
38140     // private
38141     deactivate : function(){
38142         this.fireEvent("deactivate", this);
38143     },
38144
38145     // private
38146     disable : function(){
38147         this.component.disable();
38148         Roo.menu.Adapter.superclass.disable.call(this);
38149     },
38150
38151     // private
38152     enable : function(){
38153         this.component.enable();
38154         Roo.menu.Adapter.superclass.enable.call(this);
38155     }
38156 });/*
38157  * Based on:
38158  * Ext JS Library 1.1.1
38159  * Copyright(c) 2006-2007, Ext JS, LLC.
38160  *
38161  * Originally Released Under LGPL - original licence link has changed is not relivant.
38162  *
38163  * Fork - LGPL
38164  * <script type="text/javascript">
38165  */
38166
38167 /**
38168  * @class Roo.menu.TextItem
38169  * @extends Roo.menu.BaseItem
38170  * Adds a static text string to a menu, usually used as either a heading or group separator.
38171  * Note: old style constructor with text is still supported.
38172  * 
38173  * @constructor
38174  * Creates a new TextItem
38175  * @param {Object} cfg Configuration
38176  */
38177 Roo.menu.TextItem = function(cfg){
38178     if (typeof(cfg) == 'string') {
38179         this.text = cfg;
38180     } else {
38181         Roo.apply(this,cfg);
38182     }
38183     
38184     Roo.menu.TextItem.superclass.constructor.call(this);
38185 };
38186
38187 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38188     /**
38189      * @cfg {Boolean} text Text to show on item.
38190      */
38191     text : '',
38192     
38193     /**
38194      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38195      */
38196     hideOnClick : false,
38197     /**
38198      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38199      */
38200     itemCls : "x-menu-text",
38201
38202     // private
38203     onRender : function(){
38204         var s = document.createElement("span");
38205         s.className = this.itemCls;
38206         s.innerHTML = this.text;
38207         this.el = s;
38208         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38209     }
38210 });/*
38211  * Based on:
38212  * Ext JS Library 1.1.1
38213  * Copyright(c) 2006-2007, Ext JS, LLC.
38214  *
38215  * Originally Released Under LGPL - original licence link has changed is not relivant.
38216  *
38217  * Fork - LGPL
38218  * <script type="text/javascript">
38219  */
38220
38221 /**
38222  * @class Roo.menu.Separator
38223  * @extends Roo.menu.BaseItem
38224  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38225  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38226  * @constructor
38227  * @param {Object} config Configuration options
38228  */
38229 Roo.menu.Separator = function(config){
38230     Roo.menu.Separator.superclass.constructor.call(this, config);
38231 };
38232
38233 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38234     /**
38235      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38236      */
38237     itemCls : "x-menu-sep",
38238     /**
38239      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38240      */
38241     hideOnClick : false,
38242
38243     // private
38244     onRender : function(li){
38245         var s = document.createElement("span");
38246         s.className = this.itemCls;
38247         s.innerHTML = "&#160;";
38248         this.el = s;
38249         li.addClass("x-menu-sep-li");
38250         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38251     }
38252 });/*
38253  * Based on:
38254  * Ext JS Library 1.1.1
38255  * Copyright(c) 2006-2007, Ext JS, LLC.
38256  *
38257  * Originally Released Under LGPL - original licence link has changed is not relivant.
38258  *
38259  * Fork - LGPL
38260  * <script type="text/javascript">
38261  */
38262 /**
38263  * @class Roo.menu.Item
38264  * @extends Roo.menu.BaseItem
38265  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38266  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38267  * activation and click handling.
38268  * @constructor
38269  * Creates a new Item
38270  * @param {Object} config Configuration options
38271  */
38272 Roo.menu.Item = function(config){
38273     Roo.menu.Item.superclass.constructor.call(this, config);
38274     if(this.menu){
38275         this.menu = Roo.menu.MenuMgr.get(this.menu);
38276     }
38277 };
38278 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38279     
38280     /**
38281      * @cfg {String} text
38282      * The text to show on the menu item.
38283      */
38284     text: '',
38285      /**
38286      * @cfg {String} HTML to render in menu
38287      * The text to show on the menu item (HTML version).
38288      */
38289     html: '',
38290     /**
38291      * @cfg {String} icon
38292      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38293      */
38294     icon: undefined,
38295     /**
38296      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38297      */
38298     itemCls : "x-menu-item",
38299     /**
38300      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38301      */
38302     canActivate : true,
38303     /**
38304      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38305      */
38306     showDelay: 200,
38307     // doc'd in BaseItem
38308     hideDelay: 200,
38309
38310     // private
38311     ctype: "Roo.menu.Item",
38312     
38313     // private
38314     onRender : function(container, position){
38315         var el = document.createElement("a");
38316         el.hideFocus = true;
38317         el.unselectable = "on";
38318         el.href = this.href || "#";
38319         if(this.hrefTarget){
38320             el.target = this.hrefTarget;
38321         }
38322         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38323         
38324         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38325         
38326         el.innerHTML = String.format(
38327                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38328                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38329         this.el = el;
38330         Roo.menu.Item.superclass.onRender.call(this, container, position);
38331     },
38332
38333     /**
38334      * Sets the text to display in this menu item
38335      * @param {String} text The text to display
38336      * @param {Boolean} isHTML true to indicate text is pure html.
38337      */
38338     setText : function(text, isHTML){
38339         if (isHTML) {
38340             this.html = text;
38341         } else {
38342             this.text = text;
38343             this.html = '';
38344         }
38345         if(this.rendered){
38346             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38347      
38348             this.el.update(String.format(
38349                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38350                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38351             this.parentMenu.autoWidth();
38352         }
38353     },
38354
38355     // private
38356     handleClick : function(e){
38357         if(!this.href){ // if no link defined, stop the event automatically
38358             e.stopEvent();
38359         }
38360         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38361     },
38362
38363     // private
38364     activate : function(autoExpand){
38365         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38366             this.focus();
38367             if(autoExpand){
38368                 this.expandMenu();
38369             }
38370         }
38371         return true;
38372     },
38373
38374     // private
38375     shouldDeactivate : function(e){
38376         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38377             if(this.menu && this.menu.isVisible()){
38378                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38379             }
38380             return true;
38381         }
38382         return false;
38383     },
38384
38385     // private
38386     deactivate : function(){
38387         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38388         this.hideMenu();
38389     },
38390
38391     // private
38392     expandMenu : function(autoActivate){
38393         if(!this.disabled && this.menu){
38394             clearTimeout(this.hideTimer);
38395             delete this.hideTimer;
38396             if(!this.menu.isVisible() && !this.showTimer){
38397                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38398             }else if (this.menu.isVisible() && autoActivate){
38399                 this.menu.tryActivate(0, 1);
38400             }
38401         }
38402     },
38403
38404     // private
38405     deferExpand : function(autoActivate){
38406         delete this.showTimer;
38407         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38408         if(autoActivate){
38409             this.menu.tryActivate(0, 1);
38410         }
38411     },
38412
38413     // private
38414     hideMenu : function(){
38415         clearTimeout(this.showTimer);
38416         delete this.showTimer;
38417         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38418             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38419         }
38420     },
38421
38422     // private
38423     deferHide : function(){
38424         delete this.hideTimer;
38425         this.menu.hide();
38426     }
38427 });/*
38428  * Based on:
38429  * Ext JS Library 1.1.1
38430  * Copyright(c) 2006-2007, Ext JS, LLC.
38431  *
38432  * Originally Released Under LGPL - original licence link has changed is not relivant.
38433  *
38434  * Fork - LGPL
38435  * <script type="text/javascript">
38436  */
38437  
38438 /**
38439  * @class Roo.menu.CheckItem
38440  * @extends Roo.menu.Item
38441  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38442  * @constructor
38443  * Creates a new CheckItem
38444  * @param {Object} config Configuration options
38445  */
38446 Roo.menu.CheckItem = function(config){
38447     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38448     this.addEvents({
38449         /**
38450          * @event beforecheckchange
38451          * Fires before the checked value is set, providing an opportunity to cancel if needed
38452          * @param {Roo.menu.CheckItem} this
38453          * @param {Boolean} checked The new checked value that will be set
38454          */
38455         "beforecheckchange" : true,
38456         /**
38457          * @event checkchange
38458          * Fires after the checked value has been set
38459          * @param {Roo.menu.CheckItem} this
38460          * @param {Boolean} checked The checked value that was set
38461          */
38462         "checkchange" : true
38463     });
38464     if(this.checkHandler){
38465         this.on('checkchange', this.checkHandler, this.scope);
38466     }
38467 };
38468 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38469     /**
38470      * @cfg {String} group
38471      * All check items with the same group name will automatically be grouped into a single-select
38472      * radio button group (defaults to '')
38473      */
38474     /**
38475      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38476      */
38477     itemCls : "x-menu-item x-menu-check-item",
38478     /**
38479      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38480      */
38481     groupClass : "x-menu-group-item",
38482
38483     /**
38484      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38485      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38486      * initialized with checked = true will be rendered as checked.
38487      */
38488     checked: false,
38489
38490     // private
38491     ctype: "Roo.menu.CheckItem",
38492
38493     // private
38494     onRender : function(c){
38495         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38496         if(this.group){
38497             this.el.addClass(this.groupClass);
38498         }
38499         Roo.menu.MenuMgr.registerCheckable(this);
38500         if(this.checked){
38501             this.checked = false;
38502             this.setChecked(true, true);
38503         }
38504     },
38505
38506     // private
38507     destroy : function(){
38508         if(this.rendered){
38509             Roo.menu.MenuMgr.unregisterCheckable(this);
38510         }
38511         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38512     },
38513
38514     /**
38515      * Set the checked state of this item
38516      * @param {Boolean} checked The new checked value
38517      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38518      */
38519     setChecked : function(state, suppressEvent){
38520         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38521             if(this.container){
38522                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38523             }
38524             this.checked = state;
38525             if(suppressEvent !== true){
38526                 this.fireEvent("checkchange", this, state);
38527             }
38528         }
38529     },
38530
38531     // private
38532     handleClick : function(e){
38533        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38534            this.setChecked(!this.checked);
38535        }
38536        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38537     }
38538 });/*
38539  * Based on:
38540  * Ext JS Library 1.1.1
38541  * Copyright(c) 2006-2007, Ext JS, LLC.
38542  *
38543  * Originally Released Under LGPL - original licence link has changed is not relivant.
38544  *
38545  * Fork - LGPL
38546  * <script type="text/javascript">
38547  */
38548  
38549 /**
38550  * @class Roo.menu.DateItem
38551  * @extends Roo.menu.Adapter
38552  * A menu item that wraps the {@link Roo.DatPicker} component.
38553  * @constructor
38554  * Creates a new DateItem
38555  * @param {Object} config Configuration options
38556  */
38557 Roo.menu.DateItem = function(config){
38558     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38559     /** The Roo.DatePicker object @type Roo.DatePicker */
38560     this.picker = this.component;
38561     this.addEvents({select: true});
38562     
38563     this.picker.on("render", function(picker){
38564         picker.getEl().swallowEvent("click");
38565         picker.container.addClass("x-menu-date-item");
38566     });
38567
38568     this.picker.on("select", this.onSelect, this);
38569 };
38570
38571 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38572     // private
38573     onSelect : function(picker, date){
38574         this.fireEvent("select", this, date, picker);
38575         Roo.menu.DateItem.superclass.handleClick.call(this);
38576     }
38577 });/*
38578  * Based on:
38579  * Ext JS Library 1.1.1
38580  * Copyright(c) 2006-2007, Ext JS, LLC.
38581  *
38582  * Originally Released Under LGPL - original licence link has changed is not relivant.
38583  *
38584  * Fork - LGPL
38585  * <script type="text/javascript">
38586  */
38587  
38588 /**
38589  * @class Roo.menu.ColorItem
38590  * @extends Roo.menu.Adapter
38591  * A menu item that wraps the {@link Roo.ColorPalette} component.
38592  * @constructor
38593  * Creates a new ColorItem
38594  * @param {Object} config Configuration options
38595  */
38596 Roo.menu.ColorItem = function(config){
38597     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38598     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38599     this.palette = this.component;
38600     this.relayEvents(this.palette, ["select"]);
38601     if(this.selectHandler){
38602         this.on('select', this.selectHandler, this.scope);
38603     }
38604 };
38605 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38606  * Based on:
38607  * Ext JS Library 1.1.1
38608  * Copyright(c) 2006-2007, Ext JS, LLC.
38609  *
38610  * Originally Released Under LGPL - original licence link has changed is not relivant.
38611  *
38612  * Fork - LGPL
38613  * <script type="text/javascript">
38614  */
38615  
38616
38617 /**
38618  * @class Roo.menu.DateMenu
38619  * @extends Roo.menu.Menu
38620  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38621  * @constructor
38622  * Creates a new DateMenu
38623  * @param {Object} config Configuration options
38624  */
38625 Roo.menu.DateMenu = function(config){
38626     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38627     this.plain = true;
38628     var di = new Roo.menu.DateItem(config);
38629     this.add(di);
38630     /**
38631      * The {@link Roo.DatePicker} instance for this DateMenu
38632      * @type DatePicker
38633      */
38634     this.picker = di.picker;
38635     /**
38636      * @event select
38637      * @param {DatePicker} picker
38638      * @param {Date} date
38639      */
38640     this.relayEvents(di, ["select"]);
38641     this.on('beforeshow', function(){
38642         if(this.picker){
38643             this.picker.hideMonthPicker(false);
38644         }
38645     }, this);
38646 };
38647 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38648     cls:'x-date-menu'
38649 });/*
38650  * Based on:
38651  * Ext JS Library 1.1.1
38652  * Copyright(c) 2006-2007, Ext JS, LLC.
38653  *
38654  * Originally Released Under LGPL - original licence link has changed is not relivant.
38655  *
38656  * Fork - LGPL
38657  * <script type="text/javascript">
38658  */
38659  
38660
38661 /**
38662  * @class Roo.menu.ColorMenu
38663  * @extends Roo.menu.Menu
38664  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38665  * @constructor
38666  * Creates a new ColorMenu
38667  * @param {Object} config Configuration options
38668  */
38669 Roo.menu.ColorMenu = function(config){
38670     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38671     this.plain = true;
38672     var ci = new Roo.menu.ColorItem(config);
38673     this.add(ci);
38674     /**
38675      * The {@link Roo.ColorPalette} instance for this ColorMenu
38676      * @type ColorPalette
38677      */
38678     this.palette = ci.palette;
38679     /**
38680      * @event select
38681      * @param {ColorPalette} palette
38682      * @param {String} color
38683      */
38684     this.relayEvents(ci, ["select"]);
38685 };
38686 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38687  * Based on:
38688  * Ext JS Library 1.1.1
38689  * Copyright(c) 2006-2007, Ext JS, LLC.
38690  *
38691  * Originally Released Under LGPL - original licence link has changed is not relivant.
38692  *
38693  * Fork - LGPL
38694  * <script type="text/javascript">
38695  */
38696  
38697 /**
38698  * @class Roo.form.Field
38699  * @extends Roo.BoxComponent
38700  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38701  * @constructor
38702  * Creates a new Field
38703  * @param {Object} config Configuration options
38704  */
38705 Roo.form.Field = function(config){
38706     Roo.form.Field.superclass.constructor.call(this, config);
38707 };
38708
38709 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38710     /**
38711      * @cfg {String} fieldLabel Label to use when rendering a form.
38712      */
38713        /**
38714      * @cfg {String} qtip Mouse over tip
38715      */
38716      
38717     /**
38718      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38719      */
38720     invalidClass : "x-form-invalid",
38721     /**
38722      * @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")
38723      */
38724     invalidText : "The value in this field is invalid",
38725     /**
38726      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38727      */
38728     focusClass : "x-form-focus",
38729     /**
38730      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38731       automatic validation (defaults to "keyup").
38732      */
38733     validationEvent : "keyup",
38734     /**
38735      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38736      */
38737     validateOnBlur : true,
38738     /**
38739      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38740      */
38741     validationDelay : 250,
38742     /**
38743      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38744      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38745      */
38746     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38747     /**
38748      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38749      */
38750     fieldClass : "x-form-field",
38751     /**
38752      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38753      *<pre>
38754 Value         Description
38755 -----------   ----------------------------------------------------------------------
38756 qtip          Display a quick tip when the user hovers over the field
38757 title         Display a default browser title attribute popup
38758 under         Add a block div beneath the field containing the error text
38759 side          Add an error icon to the right of the field with a popup on hover
38760 [element id]  Add the error text directly to the innerHTML of the specified element
38761 </pre>
38762      */
38763     msgTarget : 'qtip',
38764     /**
38765      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38766      */
38767     msgFx : 'normal',
38768
38769     /**
38770      * @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.
38771      */
38772     readOnly : false,
38773
38774     /**
38775      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38776      */
38777     disabled : false,
38778
38779     /**
38780      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38781      */
38782     inputType : undefined,
38783     
38784     /**
38785      * @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).
38786          */
38787         tabIndex : undefined,
38788         
38789     // private
38790     isFormField : true,
38791
38792     // private
38793     hasFocus : false,
38794     /**
38795      * @property {Roo.Element} fieldEl
38796      * Element Containing the rendered Field (with label etc.)
38797      */
38798     /**
38799      * @cfg {Mixed} value A value to initialize this field with.
38800      */
38801     value : undefined,
38802
38803     /**
38804      * @cfg {String} name The field's HTML name attribute.
38805      */
38806     /**
38807      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38808      */
38809     // private
38810     loadedValue : false,
38811      
38812      
38813         // private ??
38814         initComponent : function(){
38815         Roo.form.Field.superclass.initComponent.call(this);
38816         this.addEvents({
38817             /**
38818              * @event focus
38819              * Fires when this field receives input focus.
38820              * @param {Roo.form.Field} this
38821              */
38822             focus : true,
38823             /**
38824              * @event blur
38825              * Fires when this field loses input focus.
38826              * @param {Roo.form.Field} this
38827              */
38828             blur : true,
38829             /**
38830              * @event specialkey
38831              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38832              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38833              * @param {Roo.form.Field} this
38834              * @param {Roo.EventObject} e The event object
38835              */
38836             specialkey : true,
38837             /**
38838              * @event change
38839              * Fires just before the field blurs if the field value has changed.
38840              * @param {Roo.form.Field} this
38841              * @param {Mixed} newValue The new value
38842              * @param {Mixed} oldValue The original value
38843              */
38844             change : true,
38845             /**
38846              * @event invalid
38847              * Fires after the field has been marked as invalid.
38848              * @param {Roo.form.Field} this
38849              * @param {String} msg The validation message
38850              */
38851             invalid : true,
38852             /**
38853              * @event valid
38854              * Fires after the field has been validated with no errors.
38855              * @param {Roo.form.Field} this
38856              */
38857             valid : true,
38858              /**
38859              * @event keyup
38860              * Fires after the key up
38861              * @param {Roo.form.Field} this
38862              * @param {Roo.EventObject}  e The event Object
38863              */
38864             keyup : true
38865         });
38866     },
38867
38868     /**
38869      * Returns the name attribute of the field if available
38870      * @return {String} name The field name
38871      */
38872     getName: function(){
38873          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38874     },
38875
38876     // private
38877     onRender : function(ct, position){
38878         Roo.form.Field.superclass.onRender.call(this, ct, position);
38879         if(!this.el){
38880             var cfg = this.getAutoCreate();
38881             if(!cfg.name){
38882                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38883             }
38884             if (!cfg.name.length) {
38885                 delete cfg.name;
38886             }
38887             if(this.inputType){
38888                 cfg.type = this.inputType;
38889             }
38890             this.el = ct.createChild(cfg, position);
38891         }
38892         var type = this.el.dom.type;
38893         if(type){
38894             if(type == 'password'){
38895                 type = 'text';
38896             }
38897             this.el.addClass('x-form-'+type);
38898         }
38899         if(this.readOnly){
38900             this.el.dom.readOnly = true;
38901         }
38902         if(this.tabIndex !== undefined){
38903             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38904         }
38905
38906         this.el.addClass([this.fieldClass, this.cls]);
38907         this.initValue();
38908     },
38909
38910     /**
38911      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38912      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38913      * @return {Roo.form.Field} this
38914      */
38915     applyTo : function(target){
38916         this.allowDomMove = false;
38917         this.el = Roo.get(target);
38918         this.render(this.el.dom.parentNode);
38919         return this;
38920     },
38921
38922     // private
38923     initValue : function(){
38924         if(this.value !== undefined){
38925             this.setValue(this.value);
38926         }else if(this.el.dom.value.length > 0){
38927             this.setValue(this.el.dom.value);
38928         }
38929     },
38930
38931     /**
38932      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38933      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38934      */
38935     isDirty : function() {
38936         if(this.disabled) {
38937             return false;
38938         }
38939         return String(this.getValue()) !== String(this.originalValue);
38940     },
38941
38942     /**
38943      * stores the current value in loadedValue
38944      */
38945     resetHasChanged : function()
38946     {
38947         this.loadedValue = String(this.getValue());
38948     },
38949     /**
38950      * checks the current value against the 'loaded' value.
38951      * Note - will return false if 'resetHasChanged' has not been called first.
38952      */
38953     hasChanged : function()
38954     {
38955         if(this.disabled || this.readOnly) {
38956             return false;
38957         }
38958         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38959     },
38960     
38961     
38962     
38963     // private
38964     afterRender : function(){
38965         Roo.form.Field.superclass.afterRender.call(this);
38966         this.initEvents();
38967     },
38968
38969     // private
38970     fireKey : function(e){
38971         //Roo.log('field ' + e.getKey());
38972         if(e.isNavKeyPress()){
38973             this.fireEvent("specialkey", this, e);
38974         }
38975     },
38976
38977     /**
38978      * Resets the current field value to the originally loaded value and clears any validation messages
38979      */
38980     reset : function(){
38981         this.setValue(this.resetValue);
38982         this.originalValue = this.getValue();
38983         this.clearInvalid();
38984     },
38985
38986     // private
38987     initEvents : function(){
38988         // safari killled keypress - so keydown is now used..
38989         this.el.on("keydown" , this.fireKey,  this);
38990         this.el.on("focus", this.onFocus,  this);
38991         this.el.on("blur", this.onBlur,  this);
38992         this.el.relayEvent('keyup', this);
38993
38994         // reference to original value for reset
38995         this.originalValue = this.getValue();
38996         this.resetValue =  this.getValue();
38997     },
38998
38999     // private
39000     onFocus : function(){
39001         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39002             this.el.addClass(this.focusClass);
39003         }
39004         if(!this.hasFocus){
39005             this.hasFocus = true;
39006             this.startValue = this.getValue();
39007             this.fireEvent("focus", this);
39008         }
39009     },
39010
39011     beforeBlur : Roo.emptyFn,
39012
39013     // private
39014     onBlur : function(){
39015         this.beforeBlur();
39016         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39017             this.el.removeClass(this.focusClass);
39018         }
39019         this.hasFocus = false;
39020         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39021             this.validate();
39022         }
39023         var v = this.getValue();
39024         if(String(v) !== String(this.startValue)){
39025             this.fireEvent('change', this, v, this.startValue);
39026         }
39027         this.fireEvent("blur", this);
39028     },
39029
39030     /**
39031      * Returns whether or not the field value is currently valid
39032      * @param {Boolean} preventMark True to disable marking the field invalid
39033      * @return {Boolean} True if the value is valid, else false
39034      */
39035     isValid : function(preventMark){
39036         if(this.disabled){
39037             return true;
39038         }
39039         var restore = this.preventMark;
39040         this.preventMark = preventMark === true;
39041         var v = this.validateValue(this.processValue(this.getRawValue()));
39042         this.preventMark = restore;
39043         return v;
39044     },
39045
39046     /**
39047      * Validates the field value
39048      * @return {Boolean} True if the value is valid, else false
39049      */
39050     validate : function(){
39051         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39052             this.clearInvalid();
39053             return true;
39054         }
39055         return false;
39056     },
39057
39058     processValue : function(value){
39059         return value;
39060     },
39061
39062     // private
39063     // Subclasses should provide the validation implementation by overriding this
39064     validateValue : function(value){
39065         return true;
39066     },
39067
39068     /**
39069      * Mark this field as invalid
39070      * @param {String} msg The validation message
39071      */
39072     markInvalid : function(msg){
39073         if(!this.rendered || this.preventMark){ // not rendered
39074             return;
39075         }
39076         
39077         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39078         
39079         obj.el.addClass(this.invalidClass);
39080         msg = msg || this.invalidText;
39081         switch(this.msgTarget){
39082             case 'qtip':
39083                 obj.el.dom.qtip = msg;
39084                 obj.el.dom.qclass = 'x-form-invalid-tip';
39085                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39086                     Roo.QuickTips.enable();
39087                 }
39088                 break;
39089             case 'title':
39090                 this.el.dom.title = msg;
39091                 break;
39092             case 'under':
39093                 if(!this.errorEl){
39094                     var elp = this.el.findParent('.x-form-element', 5, true);
39095                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39096                     this.errorEl.setWidth(elp.getWidth(true)-20);
39097                 }
39098                 this.errorEl.update(msg);
39099                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39100                 break;
39101             case 'side':
39102                 if(!this.errorIcon){
39103                     var elp = this.el.findParent('.x-form-element', 5, true);
39104                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39105                 }
39106                 this.alignErrorIcon();
39107                 this.errorIcon.dom.qtip = msg;
39108                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39109                 this.errorIcon.show();
39110                 this.on('resize', this.alignErrorIcon, this);
39111                 break;
39112             default:
39113                 var t = Roo.getDom(this.msgTarget);
39114                 t.innerHTML = msg;
39115                 t.style.display = this.msgDisplay;
39116                 break;
39117         }
39118         this.fireEvent('invalid', this, msg);
39119     },
39120
39121     // private
39122     alignErrorIcon : function(){
39123         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39124     },
39125
39126     /**
39127      * Clear any invalid styles/messages for this field
39128      */
39129     clearInvalid : function(){
39130         if(!this.rendered || this.preventMark){ // not rendered
39131             return;
39132         }
39133         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39134         
39135         obj.el.removeClass(this.invalidClass);
39136         switch(this.msgTarget){
39137             case 'qtip':
39138                 obj.el.dom.qtip = '';
39139                 break;
39140             case 'title':
39141                 this.el.dom.title = '';
39142                 break;
39143             case 'under':
39144                 if(this.errorEl){
39145                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39146                 }
39147                 break;
39148             case 'side':
39149                 if(this.errorIcon){
39150                     this.errorIcon.dom.qtip = '';
39151                     this.errorIcon.hide();
39152                     this.un('resize', this.alignErrorIcon, this);
39153                 }
39154                 break;
39155             default:
39156                 var t = Roo.getDom(this.msgTarget);
39157                 t.innerHTML = '';
39158                 t.style.display = 'none';
39159                 break;
39160         }
39161         this.fireEvent('valid', this);
39162     },
39163
39164     /**
39165      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39166      * @return {Mixed} value The field value
39167      */
39168     getRawValue : function(){
39169         var v = this.el.getValue();
39170         
39171         return v;
39172     },
39173
39174     /**
39175      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39176      * @return {Mixed} value The field value
39177      */
39178     getValue : function(){
39179         var v = this.el.getValue();
39180          
39181         return v;
39182     },
39183
39184     /**
39185      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39186      * @param {Mixed} value The value to set
39187      */
39188     setRawValue : function(v){
39189         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39190     },
39191
39192     /**
39193      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39194      * @param {Mixed} value The value to set
39195      */
39196     setValue : function(v){
39197         this.value = v;
39198         if(this.rendered){
39199             this.el.dom.value = (v === null || v === undefined ? '' : v);
39200              this.validate();
39201         }
39202     },
39203
39204     adjustSize : function(w, h){
39205         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39206         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39207         return s;
39208     },
39209
39210     adjustWidth : function(tag, w){
39211         tag = tag.toLowerCase();
39212         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39213             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39214                 if(tag == 'input'){
39215                     return w + 2;
39216                 }
39217                 if(tag == 'textarea'){
39218                     return w-2;
39219                 }
39220             }else if(Roo.isOpera){
39221                 if(tag == 'input'){
39222                     return w + 2;
39223                 }
39224                 if(tag == 'textarea'){
39225                     return w-2;
39226                 }
39227             }
39228         }
39229         return w;
39230     }
39231 });
39232
39233
39234 // anything other than normal should be considered experimental
39235 Roo.form.Field.msgFx = {
39236     normal : {
39237         show: function(msgEl, f){
39238             msgEl.setDisplayed('block');
39239         },
39240
39241         hide : function(msgEl, f){
39242             msgEl.setDisplayed(false).update('');
39243         }
39244     },
39245
39246     slide : {
39247         show: function(msgEl, f){
39248             msgEl.slideIn('t', {stopFx:true});
39249         },
39250
39251         hide : function(msgEl, f){
39252             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39253         }
39254     },
39255
39256     slideRight : {
39257         show: function(msgEl, f){
39258             msgEl.fixDisplay();
39259             msgEl.alignTo(f.el, 'tl-tr');
39260             msgEl.slideIn('l', {stopFx:true});
39261         },
39262
39263         hide : function(msgEl, f){
39264             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39265         }
39266     }
39267 };/*
39268  * Based on:
39269  * Ext JS Library 1.1.1
39270  * Copyright(c) 2006-2007, Ext JS, LLC.
39271  *
39272  * Originally Released Under LGPL - original licence link has changed is not relivant.
39273  *
39274  * Fork - LGPL
39275  * <script type="text/javascript">
39276  */
39277  
39278
39279 /**
39280  * @class Roo.form.TextField
39281  * @extends Roo.form.Field
39282  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39283  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39284  * @constructor
39285  * Creates a new TextField
39286  * @param {Object} config Configuration options
39287  */
39288 Roo.form.TextField = function(config){
39289     Roo.form.TextField.superclass.constructor.call(this, config);
39290     this.addEvents({
39291         /**
39292          * @event autosize
39293          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39294          * according to the default logic, but this event provides a hook for the developer to apply additional
39295          * logic at runtime to resize the field if needed.
39296              * @param {Roo.form.Field} this This text field
39297              * @param {Number} width The new field width
39298              */
39299         autosize : true
39300     });
39301 };
39302
39303 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39304     /**
39305      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39306      */
39307     grow : false,
39308     /**
39309      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39310      */
39311     growMin : 30,
39312     /**
39313      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39314      */
39315     growMax : 800,
39316     /**
39317      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39318      */
39319     vtype : null,
39320     /**
39321      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39322      */
39323     maskRe : null,
39324     /**
39325      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39326      */
39327     disableKeyFilter : false,
39328     /**
39329      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39330      */
39331     allowBlank : true,
39332     /**
39333      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39334      */
39335     minLength : 0,
39336     /**
39337      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39338      */
39339     maxLength : Number.MAX_VALUE,
39340     /**
39341      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39342      */
39343     minLengthText : "The minimum length for this field is {0}",
39344     /**
39345      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39346      */
39347     maxLengthText : "The maximum length for this field is {0}",
39348     /**
39349      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39350      */
39351     selectOnFocus : false,
39352     /**
39353      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39354      */
39355     blankText : "This field is required",
39356     /**
39357      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39358      * If available, this function will be called only after the basic validators all return true, and will be passed the
39359      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39360      */
39361     validator : null,
39362     /**
39363      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39364      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39365      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39366      */
39367     regex : null,
39368     /**
39369      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39370      */
39371     regexText : "",
39372     /**
39373      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39374      */
39375     emptyText : null,
39376    
39377
39378     // private
39379     initEvents : function()
39380     {
39381         if (this.emptyText) {
39382             this.el.attr('placeholder', this.emptyText);
39383         }
39384         
39385         Roo.form.TextField.superclass.initEvents.call(this);
39386         if(this.validationEvent == 'keyup'){
39387             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39388             this.el.on('keyup', this.filterValidation, this);
39389         }
39390         else if(this.validationEvent !== false){
39391             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39392         }
39393         
39394         if(this.selectOnFocus){
39395             this.on("focus", this.preFocus, this);
39396             
39397         }
39398         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39399             this.el.on("keypress", this.filterKeys, this);
39400         }
39401         if(this.grow){
39402             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39403             this.el.on("click", this.autoSize,  this);
39404         }
39405         if(this.el.is('input[type=password]') && Roo.isSafari){
39406             this.el.on('keydown', this.SafariOnKeyDown, this);
39407         }
39408     },
39409
39410     processValue : function(value){
39411         if(this.stripCharsRe){
39412             var newValue = value.replace(this.stripCharsRe, '');
39413             if(newValue !== value){
39414                 this.setRawValue(newValue);
39415                 return newValue;
39416             }
39417         }
39418         return value;
39419     },
39420
39421     filterValidation : function(e){
39422         if(!e.isNavKeyPress()){
39423             this.validationTask.delay(this.validationDelay);
39424         }
39425     },
39426
39427     // private
39428     onKeyUp : function(e){
39429         if(!e.isNavKeyPress()){
39430             this.autoSize();
39431         }
39432     },
39433
39434     /**
39435      * Resets the current field value to the originally-loaded value and clears any validation messages.
39436      *  
39437      */
39438     reset : function(){
39439         Roo.form.TextField.superclass.reset.call(this);
39440        
39441     },
39442
39443     
39444     // private
39445     preFocus : function(){
39446         
39447         if(this.selectOnFocus){
39448             this.el.dom.select();
39449         }
39450     },
39451
39452     
39453     // private
39454     filterKeys : function(e){
39455         var k = e.getKey();
39456         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39457             return;
39458         }
39459         var c = e.getCharCode(), cc = String.fromCharCode(c);
39460         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39461             return;
39462         }
39463         if(!this.maskRe.test(cc)){
39464             e.stopEvent();
39465         }
39466     },
39467
39468     setValue : function(v){
39469         
39470         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39471         
39472         this.autoSize();
39473     },
39474
39475     /**
39476      * Validates a value according to the field's validation rules and marks the field as invalid
39477      * if the validation fails
39478      * @param {Mixed} value The value to validate
39479      * @return {Boolean} True if the value is valid, else false
39480      */
39481     validateValue : function(value){
39482         if(value.length < 1)  { // if it's blank
39483              if(this.allowBlank){
39484                 this.clearInvalid();
39485                 return true;
39486              }else{
39487                 this.markInvalid(this.blankText);
39488                 return false;
39489              }
39490         }
39491         if(value.length < this.minLength){
39492             this.markInvalid(String.format(this.minLengthText, this.minLength));
39493             return false;
39494         }
39495         if(value.length > this.maxLength){
39496             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39497             return false;
39498         }
39499         if(this.vtype){
39500             var vt = Roo.form.VTypes;
39501             if(!vt[this.vtype](value, this)){
39502                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39503                 return false;
39504             }
39505         }
39506         if(typeof this.validator == "function"){
39507             var msg = this.validator(value);
39508             if(msg !== true){
39509                 this.markInvalid(msg);
39510                 return false;
39511             }
39512         }
39513         if(this.regex && !this.regex.test(value)){
39514             this.markInvalid(this.regexText);
39515             return false;
39516         }
39517         return true;
39518     },
39519
39520     /**
39521      * Selects text in this field
39522      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39523      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39524      */
39525     selectText : function(start, end){
39526         var v = this.getRawValue();
39527         if(v.length > 0){
39528             start = start === undefined ? 0 : start;
39529             end = end === undefined ? v.length : end;
39530             var d = this.el.dom;
39531             if(d.setSelectionRange){
39532                 d.setSelectionRange(start, end);
39533             }else if(d.createTextRange){
39534                 var range = d.createTextRange();
39535                 range.moveStart("character", start);
39536                 range.moveEnd("character", v.length-end);
39537                 range.select();
39538             }
39539         }
39540     },
39541
39542     /**
39543      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39544      * This only takes effect if grow = true, and fires the autosize event.
39545      */
39546     autoSize : function(){
39547         if(!this.grow || !this.rendered){
39548             return;
39549         }
39550         if(!this.metrics){
39551             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39552         }
39553         var el = this.el;
39554         var v = el.dom.value;
39555         var d = document.createElement('div');
39556         d.appendChild(document.createTextNode(v));
39557         v = d.innerHTML;
39558         d = null;
39559         v += "&#160;";
39560         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39561         this.el.setWidth(w);
39562         this.fireEvent("autosize", this, w);
39563     },
39564     
39565     // private
39566     SafariOnKeyDown : function(event)
39567     {
39568         // this is a workaround for a password hang bug on chrome/ webkit.
39569         
39570         var isSelectAll = false;
39571         
39572         if(this.el.dom.selectionEnd > 0){
39573             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39574         }
39575         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39576             event.preventDefault();
39577             this.setValue('');
39578             return;
39579         }
39580         
39581         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39582             
39583             event.preventDefault();
39584             // this is very hacky as keydown always get's upper case.
39585             
39586             var cc = String.fromCharCode(event.getCharCode());
39587             
39588             
39589             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39590             
39591         }
39592         
39593         
39594     }
39595 });/*
39596  * Based on:
39597  * Ext JS Library 1.1.1
39598  * Copyright(c) 2006-2007, Ext JS, LLC.
39599  *
39600  * Originally Released Under LGPL - original licence link has changed is not relivant.
39601  *
39602  * Fork - LGPL
39603  * <script type="text/javascript">
39604  */
39605  
39606 /**
39607  * @class Roo.form.Hidden
39608  * @extends Roo.form.TextField
39609  * Simple Hidden element used on forms 
39610  * 
39611  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39612  * 
39613  * @constructor
39614  * Creates a new Hidden form element.
39615  * @param {Object} config Configuration options
39616  */
39617
39618
39619
39620 // easy hidden field...
39621 Roo.form.Hidden = function(config){
39622     Roo.form.Hidden.superclass.constructor.call(this, config);
39623 };
39624   
39625 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39626     fieldLabel:      '',
39627     inputType:      'hidden',
39628     width:          50,
39629     allowBlank:     true,
39630     labelSeparator: '',
39631     hidden:         true,
39632     itemCls :       'x-form-item-display-none'
39633
39634
39635 });
39636
39637
39638 /*
39639  * Based on:
39640  * Ext JS Library 1.1.1
39641  * Copyright(c) 2006-2007, Ext JS, LLC.
39642  *
39643  * Originally Released Under LGPL - original licence link has changed is not relivant.
39644  *
39645  * Fork - LGPL
39646  * <script type="text/javascript">
39647  */
39648  
39649 /**
39650  * @class Roo.form.TriggerField
39651  * @extends Roo.form.TextField
39652  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39653  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39654  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39655  * for which you can provide a custom implementation.  For example:
39656  * <pre><code>
39657 var trigger = new Roo.form.TriggerField();
39658 trigger.onTriggerClick = myTriggerFn;
39659 trigger.applyTo('my-field');
39660 </code></pre>
39661  *
39662  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39663  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39664  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39665  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39666  * @constructor
39667  * Create a new TriggerField.
39668  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39669  * to the base TextField)
39670  */
39671 Roo.form.TriggerField = function(config){
39672     this.mimicing = false;
39673     Roo.form.TriggerField.superclass.constructor.call(this, config);
39674 };
39675
39676 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39677     /**
39678      * @cfg {String} triggerClass A CSS class to apply to the trigger
39679      */
39680     /**
39681      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39682      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39683      */
39684     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39685     /**
39686      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39687      */
39688     hideTrigger:false,
39689
39690     /** @cfg {Boolean} grow @hide */
39691     /** @cfg {Number} growMin @hide */
39692     /** @cfg {Number} growMax @hide */
39693
39694     /**
39695      * @hide 
39696      * @method
39697      */
39698     autoSize: Roo.emptyFn,
39699     // private
39700     monitorTab : true,
39701     // private
39702     deferHeight : true,
39703
39704     
39705     actionMode : 'wrap',
39706     // private
39707     onResize : function(w, h){
39708         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39709         if(typeof w == 'number'){
39710             var x = w - this.trigger.getWidth();
39711             this.el.setWidth(this.adjustWidth('input', x));
39712             this.trigger.setStyle('left', x+'px');
39713         }
39714     },
39715
39716     // private
39717     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39718
39719     // private
39720     getResizeEl : function(){
39721         return this.wrap;
39722     },
39723
39724     // private
39725     getPositionEl : function(){
39726         return this.wrap;
39727     },
39728
39729     // private
39730     alignErrorIcon : function(){
39731         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39732     },
39733
39734     // private
39735     onRender : function(ct, position){
39736         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39737         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39738         this.trigger = this.wrap.createChild(this.triggerConfig ||
39739                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39740         if(this.hideTrigger){
39741             this.trigger.setDisplayed(false);
39742         }
39743         this.initTrigger();
39744         if(!this.width){
39745             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39746         }
39747     },
39748
39749     // private
39750     initTrigger : function(){
39751         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39752         this.trigger.addClassOnOver('x-form-trigger-over');
39753         this.trigger.addClassOnClick('x-form-trigger-click');
39754     },
39755
39756     // private
39757     onDestroy : function(){
39758         if(this.trigger){
39759             this.trigger.removeAllListeners();
39760             this.trigger.remove();
39761         }
39762         if(this.wrap){
39763             this.wrap.remove();
39764         }
39765         Roo.form.TriggerField.superclass.onDestroy.call(this);
39766     },
39767
39768     // private
39769     onFocus : function(){
39770         Roo.form.TriggerField.superclass.onFocus.call(this);
39771         if(!this.mimicing){
39772             this.wrap.addClass('x-trigger-wrap-focus');
39773             this.mimicing = true;
39774             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39775             if(this.monitorTab){
39776                 this.el.on("keydown", this.checkTab, this);
39777             }
39778         }
39779     },
39780
39781     // private
39782     checkTab : function(e){
39783         if(e.getKey() == e.TAB){
39784             this.triggerBlur();
39785         }
39786     },
39787
39788     // private
39789     onBlur : function(){
39790         // do nothing
39791     },
39792
39793     // private
39794     mimicBlur : function(e, t){
39795         if(!this.wrap.contains(t) && this.validateBlur()){
39796             this.triggerBlur();
39797         }
39798     },
39799
39800     // private
39801     triggerBlur : function(){
39802         this.mimicing = false;
39803         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39804         if(this.monitorTab){
39805             this.el.un("keydown", this.checkTab, this);
39806         }
39807         this.wrap.removeClass('x-trigger-wrap-focus');
39808         Roo.form.TriggerField.superclass.onBlur.call(this);
39809     },
39810
39811     // private
39812     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39813     validateBlur : function(e, t){
39814         return true;
39815     },
39816
39817     // private
39818     onDisable : function(){
39819         Roo.form.TriggerField.superclass.onDisable.call(this);
39820         if(this.wrap){
39821             this.wrap.addClass('x-item-disabled');
39822         }
39823     },
39824
39825     // private
39826     onEnable : function(){
39827         Roo.form.TriggerField.superclass.onEnable.call(this);
39828         if(this.wrap){
39829             this.wrap.removeClass('x-item-disabled');
39830         }
39831     },
39832
39833     // private
39834     onShow : function(){
39835         var ae = this.getActionEl();
39836         
39837         if(ae){
39838             ae.dom.style.display = '';
39839             ae.dom.style.visibility = 'visible';
39840         }
39841     },
39842
39843     // private
39844     
39845     onHide : function(){
39846         var ae = this.getActionEl();
39847         ae.dom.style.display = 'none';
39848     },
39849
39850     /**
39851      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39852      * by an implementing function.
39853      * @method
39854      * @param {EventObject} e
39855      */
39856     onTriggerClick : Roo.emptyFn
39857 });
39858
39859 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39860 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39861 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39862 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39863     initComponent : function(){
39864         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39865
39866         this.triggerConfig = {
39867             tag:'span', cls:'x-form-twin-triggers', cn:[
39868             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39869             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39870         ]};
39871     },
39872
39873     getTrigger : function(index){
39874         return this.triggers[index];
39875     },
39876
39877     initTrigger : function(){
39878         var ts = this.trigger.select('.x-form-trigger', true);
39879         this.wrap.setStyle('overflow', 'hidden');
39880         var triggerField = this;
39881         ts.each(function(t, all, index){
39882             t.hide = function(){
39883                 var w = triggerField.wrap.getWidth();
39884                 this.dom.style.display = 'none';
39885                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39886             };
39887             t.show = function(){
39888                 var w = triggerField.wrap.getWidth();
39889                 this.dom.style.display = '';
39890                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39891             };
39892             var triggerIndex = 'Trigger'+(index+1);
39893
39894             if(this['hide'+triggerIndex]){
39895                 t.dom.style.display = 'none';
39896             }
39897             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39898             t.addClassOnOver('x-form-trigger-over');
39899             t.addClassOnClick('x-form-trigger-click');
39900         }, this);
39901         this.triggers = ts.elements;
39902     },
39903
39904     onTrigger1Click : Roo.emptyFn,
39905     onTrigger2Click : Roo.emptyFn
39906 });/*
39907  * Based on:
39908  * Ext JS Library 1.1.1
39909  * Copyright(c) 2006-2007, Ext JS, LLC.
39910  *
39911  * Originally Released Under LGPL - original licence link has changed is not relivant.
39912  *
39913  * Fork - LGPL
39914  * <script type="text/javascript">
39915  */
39916  
39917 /**
39918  * @class Roo.form.TextArea
39919  * @extends Roo.form.TextField
39920  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39921  * support for auto-sizing.
39922  * @constructor
39923  * Creates a new TextArea
39924  * @param {Object} config Configuration options
39925  */
39926 Roo.form.TextArea = function(config){
39927     Roo.form.TextArea.superclass.constructor.call(this, config);
39928     // these are provided exchanges for backwards compat
39929     // minHeight/maxHeight were replaced by growMin/growMax to be
39930     // compatible with TextField growing config values
39931     if(this.minHeight !== undefined){
39932         this.growMin = this.minHeight;
39933     }
39934     if(this.maxHeight !== undefined){
39935         this.growMax = this.maxHeight;
39936     }
39937 };
39938
39939 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39940     /**
39941      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39942      */
39943     growMin : 60,
39944     /**
39945      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39946      */
39947     growMax: 1000,
39948     /**
39949      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39950      * in the field (equivalent to setting overflow: hidden, defaults to false)
39951      */
39952     preventScrollbars: false,
39953     /**
39954      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39955      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39956      */
39957
39958     // private
39959     onRender : function(ct, position){
39960         if(!this.el){
39961             this.defaultAutoCreate = {
39962                 tag: "textarea",
39963                 style:"width:300px;height:60px;",
39964                 autocomplete: "new-password"
39965             };
39966         }
39967         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39968         if(this.grow){
39969             this.textSizeEl = Roo.DomHelper.append(document.body, {
39970                 tag: "pre", cls: "x-form-grow-sizer"
39971             });
39972             if(this.preventScrollbars){
39973                 this.el.setStyle("overflow", "hidden");
39974             }
39975             this.el.setHeight(this.growMin);
39976         }
39977     },
39978
39979     onDestroy : function(){
39980         if(this.textSizeEl){
39981             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39982         }
39983         Roo.form.TextArea.superclass.onDestroy.call(this);
39984     },
39985
39986     // private
39987     onKeyUp : function(e){
39988         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39989             this.autoSize();
39990         }
39991     },
39992
39993     /**
39994      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39995      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39996      */
39997     autoSize : function(){
39998         if(!this.grow || !this.textSizeEl){
39999             return;
40000         }
40001         var el = this.el;
40002         var v = el.dom.value;
40003         var ts = this.textSizeEl;
40004
40005         ts.innerHTML = '';
40006         ts.appendChild(document.createTextNode(v));
40007         v = ts.innerHTML;
40008
40009         Roo.fly(ts).setWidth(this.el.getWidth());
40010         if(v.length < 1){
40011             v = "&#160;&#160;";
40012         }else{
40013             if(Roo.isIE){
40014                 v = v.replace(/\n/g, '<p>&#160;</p>');
40015             }
40016             v += "&#160;\n&#160;";
40017         }
40018         ts.innerHTML = v;
40019         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40020         if(h != this.lastHeight){
40021             this.lastHeight = h;
40022             this.el.setHeight(h);
40023             this.fireEvent("autosize", this, h);
40024         }
40025     }
40026 });/*
40027  * Based on:
40028  * Ext JS Library 1.1.1
40029  * Copyright(c) 2006-2007, Ext JS, LLC.
40030  *
40031  * Originally Released Under LGPL - original licence link has changed is not relivant.
40032  *
40033  * Fork - LGPL
40034  * <script type="text/javascript">
40035  */
40036  
40037
40038 /**
40039  * @class Roo.form.NumberField
40040  * @extends Roo.form.TextField
40041  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40042  * @constructor
40043  * Creates a new NumberField
40044  * @param {Object} config Configuration options
40045  */
40046 Roo.form.NumberField = function(config){
40047     Roo.form.NumberField.superclass.constructor.call(this, config);
40048 };
40049
40050 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40051     /**
40052      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40053      */
40054     fieldClass: "x-form-field x-form-num-field",
40055     /**
40056      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40057      */
40058     allowDecimals : true,
40059     /**
40060      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40061      */
40062     decimalSeparator : ".",
40063     /**
40064      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40065      */
40066     decimalPrecision : 2,
40067     /**
40068      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40069      */
40070     allowNegative : true,
40071     /**
40072      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40073      */
40074     minValue : Number.NEGATIVE_INFINITY,
40075     /**
40076      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40077      */
40078     maxValue : Number.MAX_VALUE,
40079     /**
40080      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40081      */
40082     minText : "The minimum value for this field is {0}",
40083     /**
40084      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40085      */
40086     maxText : "The maximum value for this field is {0}",
40087     /**
40088      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40089      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40090      */
40091     nanText : "{0} is not a valid number",
40092
40093     // private
40094     initEvents : function(){
40095         Roo.form.NumberField.superclass.initEvents.call(this);
40096         var allowed = "0123456789";
40097         if(this.allowDecimals){
40098             allowed += this.decimalSeparator;
40099         }
40100         if(this.allowNegative){
40101             allowed += "-";
40102         }
40103         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40104         var keyPress = function(e){
40105             var k = e.getKey();
40106             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40107                 return;
40108             }
40109             var c = e.getCharCode();
40110             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40111                 e.stopEvent();
40112             }
40113         };
40114         this.el.on("keypress", keyPress, this);
40115     },
40116
40117     // private
40118     validateValue : function(value){
40119         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40120             return false;
40121         }
40122         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40123              return true;
40124         }
40125         var num = this.parseValue(value);
40126         if(isNaN(num)){
40127             this.markInvalid(String.format(this.nanText, value));
40128             return false;
40129         }
40130         if(num < this.minValue){
40131             this.markInvalid(String.format(this.minText, this.minValue));
40132             return false;
40133         }
40134         if(num > this.maxValue){
40135             this.markInvalid(String.format(this.maxText, this.maxValue));
40136             return false;
40137         }
40138         return true;
40139     },
40140
40141     getValue : function(){
40142         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40143     },
40144
40145     // private
40146     parseValue : function(value){
40147         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40148         return isNaN(value) ? '' : value;
40149     },
40150
40151     // private
40152     fixPrecision : function(value){
40153         var nan = isNaN(value);
40154         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40155             return nan ? '' : value;
40156         }
40157         return parseFloat(value).toFixed(this.decimalPrecision);
40158     },
40159
40160     setValue : function(v){
40161         v = this.fixPrecision(v);
40162         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40163     },
40164
40165     // private
40166     decimalPrecisionFcn : function(v){
40167         return Math.floor(v);
40168     },
40169
40170     beforeBlur : function(){
40171         var v = this.parseValue(this.getRawValue());
40172         if(v){
40173             this.setValue(v);
40174         }
40175     }
40176 });/*
40177  * Based on:
40178  * Ext JS Library 1.1.1
40179  * Copyright(c) 2006-2007, Ext JS, LLC.
40180  *
40181  * Originally Released Under LGPL - original licence link has changed is not relivant.
40182  *
40183  * Fork - LGPL
40184  * <script type="text/javascript">
40185  */
40186  
40187 /**
40188  * @class Roo.form.DateField
40189  * @extends Roo.form.TriggerField
40190  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40191 * @constructor
40192 * Create a new DateField
40193 * @param {Object} config
40194  */
40195 Roo.form.DateField = function(config){
40196     Roo.form.DateField.superclass.constructor.call(this, config);
40197     
40198       this.addEvents({
40199          
40200         /**
40201          * @event select
40202          * Fires when a date is selected
40203              * @param {Roo.form.DateField} combo This combo box
40204              * @param {Date} date The date selected
40205              */
40206         'select' : true
40207          
40208     });
40209     
40210     
40211     if(typeof this.minValue == "string") {
40212         this.minValue = this.parseDate(this.minValue);
40213     }
40214     if(typeof this.maxValue == "string") {
40215         this.maxValue = this.parseDate(this.maxValue);
40216     }
40217     this.ddMatch = null;
40218     if(this.disabledDates){
40219         var dd = this.disabledDates;
40220         var re = "(?:";
40221         for(var i = 0; i < dd.length; i++){
40222             re += dd[i];
40223             if(i != dd.length-1) {
40224                 re += "|";
40225             }
40226         }
40227         this.ddMatch = new RegExp(re + ")");
40228     }
40229 };
40230
40231 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40232     /**
40233      * @cfg {String} format
40234      * The default date format string which can be overriden for localization support.  The format must be
40235      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40236      */
40237     format : "m/d/y",
40238     /**
40239      * @cfg {String} altFormats
40240      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40241      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40242      */
40243     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40244     /**
40245      * @cfg {Array} disabledDays
40246      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40247      */
40248     disabledDays : null,
40249     /**
40250      * @cfg {String} disabledDaysText
40251      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40252      */
40253     disabledDaysText : "Disabled",
40254     /**
40255      * @cfg {Array} disabledDates
40256      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40257      * expression so they are very powerful. Some examples:
40258      * <ul>
40259      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40260      * <li>["03/08", "09/16"] would disable those days for every year</li>
40261      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40262      * <li>["03/../2006"] would disable every day in March 2006</li>
40263      * <li>["^03"] would disable every day in every March</li>
40264      * </ul>
40265      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40266      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40267      */
40268     disabledDates : null,
40269     /**
40270      * @cfg {String} disabledDatesText
40271      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40272      */
40273     disabledDatesText : "Disabled",
40274     /**
40275      * @cfg {Date/String} minValue
40276      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40277      * valid format (defaults to null).
40278      */
40279     minValue : null,
40280     /**
40281      * @cfg {Date/String} maxValue
40282      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40283      * valid format (defaults to null).
40284      */
40285     maxValue : null,
40286     /**
40287      * @cfg {String} minText
40288      * The error text to display when the date in the cell is before minValue (defaults to
40289      * 'The date in this field must be after {minValue}').
40290      */
40291     minText : "The date in this field must be equal to or after {0}",
40292     /**
40293      * @cfg {String} maxText
40294      * The error text to display when the date in the cell is after maxValue (defaults to
40295      * 'The date in this field must be before {maxValue}').
40296      */
40297     maxText : "The date in this field must be equal to or before {0}",
40298     /**
40299      * @cfg {String} invalidText
40300      * The error text to display when the date in the field is invalid (defaults to
40301      * '{value} is not a valid date - it must be in the format {format}').
40302      */
40303     invalidText : "{0} is not a valid date - it must be in the format {1}",
40304     /**
40305      * @cfg {String} triggerClass
40306      * An additional CSS class used to style the trigger button.  The trigger will always get the
40307      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40308      * which displays a calendar icon).
40309      */
40310     triggerClass : 'x-form-date-trigger',
40311     
40312
40313     /**
40314      * @cfg {Boolean} useIso
40315      * if enabled, then the date field will use a hidden field to store the 
40316      * real value as iso formated date. default (false)
40317      */ 
40318     useIso : false,
40319     /**
40320      * @cfg {String/Object} autoCreate
40321      * A DomHelper element spec, or true for a default element spec (defaults to
40322      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40323      */ 
40324     // private
40325     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40326     
40327     // private
40328     hiddenField: false,
40329     
40330     onRender : function(ct, position)
40331     {
40332         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40333         if (this.useIso) {
40334             //this.el.dom.removeAttribute('name'); 
40335             Roo.log("Changing name?");
40336             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40337             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40338                     'before', true);
40339             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40340             // prevent input submission
40341             this.hiddenName = this.name;
40342         }
40343             
40344             
40345     },
40346     
40347     // private
40348     validateValue : function(value)
40349     {
40350         value = this.formatDate(value);
40351         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40352             Roo.log('super failed');
40353             return false;
40354         }
40355         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40356              return true;
40357         }
40358         var svalue = value;
40359         value = this.parseDate(value);
40360         if(!value){
40361             Roo.log('parse date failed' + svalue);
40362             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40363             return false;
40364         }
40365         var time = value.getTime();
40366         if(this.minValue && time < this.minValue.getTime()){
40367             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40368             return false;
40369         }
40370         if(this.maxValue && time > this.maxValue.getTime()){
40371             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40372             return false;
40373         }
40374         if(this.disabledDays){
40375             var day = value.getDay();
40376             for(var i = 0; i < this.disabledDays.length; i++) {
40377                 if(day === this.disabledDays[i]){
40378                     this.markInvalid(this.disabledDaysText);
40379                     return false;
40380                 }
40381             }
40382         }
40383         var fvalue = this.formatDate(value);
40384         if(this.ddMatch && this.ddMatch.test(fvalue)){
40385             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40386             return false;
40387         }
40388         return true;
40389     },
40390
40391     // private
40392     // Provides logic to override the default TriggerField.validateBlur which just returns true
40393     validateBlur : function(){
40394         return !this.menu || !this.menu.isVisible();
40395     },
40396     
40397     getName: function()
40398     {
40399         // returns hidden if it's set..
40400         if (!this.rendered) {return ''};
40401         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40402         
40403     },
40404
40405     /**
40406      * Returns the current date value of the date field.
40407      * @return {Date} The date value
40408      */
40409     getValue : function(){
40410         
40411         return  this.hiddenField ?
40412                 this.hiddenField.value :
40413                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40414     },
40415
40416     /**
40417      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40418      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40419      * (the default format used is "m/d/y").
40420      * <br />Usage:
40421      * <pre><code>
40422 //All of these calls set the same date value (May 4, 2006)
40423
40424 //Pass a date object:
40425 var dt = new Date('5/4/06');
40426 dateField.setValue(dt);
40427
40428 //Pass a date string (default format):
40429 dateField.setValue('5/4/06');
40430
40431 //Pass a date string (custom format):
40432 dateField.format = 'Y-m-d';
40433 dateField.setValue('2006-5-4');
40434 </code></pre>
40435      * @param {String/Date} date The date or valid date string
40436      */
40437     setValue : function(date){
40438         if (this.hiddenField) {
40439             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40440         }
40441         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40442         // make sure the value field is always stored as a date..
40443         this.value = this.parseDate(date);
40444         
40445         
40446     },
40447
40448     // private
40449     parseDate : function(value){
40450         if(!value || value instanceof Date){
40451             return value;
40452         }
40453         var v = Date.parseDate(value, this.format);
40454          if (!v && this.useIso) {
40455             v = Date.parseDate(value, 'Y-m-d');
40456         }
40457         if(!v && this.altFormats){
40458             if(!this.altFormatsArray){
40459                 this.altFormatsArray = this.altFormats.split("|");
40460             }
40461             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40462                 v = Date.parseDate(value, this.altFormatsArray[i]);
40463             }
40464         }
40465         return v;
40466     },
40467
40468     // private
40469     formatDate : function(date, fmt){
40470         return (!date || !(date instanceof Date)) ?
40471                date : date.dateFormat(fmt || this.format);
40472     },
40473
40474     // private
40475     menuListeners : {
40476         select: function(m, d){
40477             
40478             this.setValue(d);
40479             this.fireEvent('select', this, d);
40480         },
40481         show : function(){ // retain focus styling
40482             this.onFocus();
40483         },
40484         hide : function(){
40485             this.focus.defer(10, this);
40486             var ml = this.menuListeners;
40487             this.menu.un("select", ml.select,  this);
40488             this.menu.un("show", ml.show,  this);
40489             this.menu.un("hide", ml.hide,  this);
40490         }
40491     },
40492
40493     // private
40494     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40495     onTriggerClick : function(){
40496         if(this.disabled){
40497             return;
40498         }
40499         if(this.menu == null){
40500             this.menu = new Roo.menu.DateMenu();
40501         }
40502         Roo.apply(this.menu.picker,  {
40503             showClear: this.allowBlank,
40504             minDate : this.minValue,
40505             maxDate : this.maxValue,
40506             disabledDatesRE : this.ddMatch,
40507             disabledDatesText : this.disabledDatesText,
40508             disabledDays : this.disabledDays,
40509             disabledDaysText : this.disabledDaysText,
40510             format : this.useIso ? 'Y-m-d' : this.format,
40511             minText : String.format(this.minText, this.formatDate(this.minValue)),
40512             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40513         });
40514         this.menu.on(Roo.apply({}, this.menuListeners, {
40515             scope:this
40516         }));
40517         this.menu.picker.setValue(this.getValue() || new Date());
40518         this.menu.show(this.el, "tl-bl?");
40519     },
40520
40521     beforeBlur : function(){
40522         var v = this.parseDate(this.getRawValue());
40523         if(v){
40524             this.setValue(v);
40525         }
40526     },
40527
40528     /*@
40529      * overide
40530      * 
40531      */
40532     isDirty : function() {
40533         if(this.disabled) {
40534             return false;
40535         }
40536         
40537         if(typeof(this.startValue) === 'undefined'){
40538             return false;
40539         }
40540         
40541         return String(this.getValue()) !== String(this.startValue);
40542         
40543     }
40544 });/*
40545  * Based on:
40546  * Ext JS Library 1.1.1
40547  * Copyright(c) 2006-2007, Ext JS, LLC.
40548  *
40549  * Originally Released Under LGPL - original licence link has changed is not relivant.
40550  *
40551  * Fork - LGPL
40552  * <script type="text/javascript">
40553  */
40554  
40555 /**
40556  * @class Roo.form.MonthField
40557  * @extends Roo.form.TriggerField
40558  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40559 * @constructor
40560 * Create a new MonthField
40561 * @param {Object} config
40562  */
40563 Roo.form.MonthField = function(config){
40564     
40565     Roo.form.MonthField.superclass.constructor.call(this, config);
40566     
40567       this.addEvents({
40568          
40569         /**
40570          * @event select
40571          * Fires when a date is selected
40572              * @param {Roo.form.MonthFieeld} combo This combo box
40573              * @param {Date} date The date selected
40574              */
40575         'select' : true
40576          
40577     });
40578     
40579     
40580     if(typeof this.minValue == "string") {
40581         this.minValue = this.parseDate(this.minValue);
40582     }
40583     if(typeof this.maxValue == "string") {
40584         this.maxValue = this.parseDate(this.maxValue);
40585     }
40586     this.ddMatch = null;
40587     if(this.disabledDates){
40588         var dd = this.disabledDates;
40589         var re = "(?:";
40590         for(var i = 0; i < dd.length; i++){
40591             re += dd[i];
40592             if(i != dd.length-1) {
40593                 re += "|";
40594             }
40595         }
40596         this.ddMatch = new RegExp(re + ")");
40597     }
40598 };
40599
40600 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40601     /**
40602      * @cfg {String} format
40603      * The default date format string which can be overriden for localization support.  The format must be
40604      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40605      */
40606     format : "M Y",
40607     /**
40608      * @cfg {String} altFormats
40609      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40610      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40611      */
40612     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40613     /**
40614      * @cfg {Array} disabledDays
40615      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40616      */
40617     disabledDays : [0,1,2,3,4,5,6],
40618     /**
40619      * @cfg {String} disabledDaysText
40620      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40621      */
40622     disabledDaysText : "Disabled",
40623     /**
40624      * @cfg {Array} disabledDates
40625      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40626      * expression so they are very powerful. Some examples:
40627      * <ul>
40628      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40629      * <li>["03/08", "09/16"] would disable those days for every year</li>
40630      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40631      * <li>["03/../2006"] would disable every day in March 2006</li>
40632      * <li>["^03"] would disable every day in every March</li>
40633      * </ul>
40634      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40635      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40636      */
40637     disabledDates : null,
40638     /**
40639      * @cfg {String} disabledDatesText
40640      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40641      */
40642     disabledDatesText : "Disabled",
40643     /**
40644      * @cfg {Date/String} minValue
40645      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40646      * valid format (defaults to null).
40647      */
40648     minValue : null,
40649     /**
40650      * @cfg {Date/String} maxValue
40651      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40652      * valid format (defaults to null).
40653      */
40654     maxValue : null,
40655     /**
40656      * @cfg {String} minText
40657      * The error text to display when the date in the cell is before minValue (defaults to
40658      * 'The date in this field must be after {minValue}').
40659      */
40660     minText : "The date in this field must be equal to or after {0}",
40661     /**
40662      * @cfg {String} maxTextf
40663      * The error text to display when the date in the cell is after maxValue (defaults to
40664      * 'The date in this field must be before {maxValue}').
40665      */
40666     maxText : "The date in this field must be equal to or before {0}",
40667     /**
40668      * @cfg {String} invalidText
40669      * The error text to display when the date in the field is invalid (defaults to
40670      * '{value} is not a valid date - it must be in the format {format}').
40671      */
40672     invalidText : "{0} is not a valid date - it must be in the format {1}",
40673     /**
40674      * @cfg {String} triggerClass
40675      * An additional CSS class used to style the trigger button.  The trigger will always get the
40676      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40677      * which displays a calendar icon).
40678      */
40679     triggerClass : 'x-form-date-trigger',
40680     
40681
40682     /**
40683      * @cfg {Boolean} useIso
40684      * if enabled, then the date field will use a hidden field to store the 
40685      * real value as iso formated date. default (true)
40686      */ 
40687     useIso : true,
40688     /**
40689      * @cfg {String/Object} autoCreate
40690      * A DomHelper element spec, or true for a default element spec (defaults to
40691      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40692      */ 
40693     // private
40694     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40695     
40696     // private
40697     hiddenField: false,
40698     
40699     hideMonthPicker : false,
40700     
40701     onRender : function(ct, position)
40702     {
40703         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40704         if (this.useIso) {
40705             this.el.dom.removeAttribute('name'); 
40706             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40707                     'before', true);
40708             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40709             // prevent input submission
40710             this.hiddenName = this.name;
40711         }
40712             
40713             
40714     },
40715     
40716     // private
40717     validateValue : function(value)
40718     {
40719         value = this.formatDate(value);
40720         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40721             return false;
40722         }
40723         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40724              return true;
40725         }
40726         var svalue = value;
40727         value = this.parseDate(value);
40728         if(!value){
40729             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40730             return false;
40731         }
40732         var time = value.getTime();
40733         if(this.minValue && time < this.minValue.getTime()){
40734             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40735             return false;
40736         }
40737         if(this.maxValue && time > this.maxValue.getTime()){
40738             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40739             return false;
40740         }
40741         /*if(this.disabledDays){
40742             var day = value.getDay();
40743             for(var i = 0; i < this.disabledDays.length; i++) {
40744                 if(day === this.disabledDays[i]){
40745                     this.markInvalid(this.disabledDaysText);
40746                     return false;
40747                 }
40748             }
40749         }
40750         */
40751         var fvalue = this.formatDate(value);
40752         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40753             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40754             return false;
40755         }
40756         */
40757         return true;
40758     },
40759
40760     // private
40761     // Provides logic to override the default TriggerField.validateBlur which just returns true
40762     validateBlur : function(){
40763         return !this.menu || !this.menu.isVisible();
40764     },
40765
40766     /**
40767      * Returns the current date value of the date field.
40768      * @return {Date} The date value
40769      */
40770     getValue : function(){
40771         
40772         
40773         
40774         return  this.hiddenField ?
40775                 this.hiddenField.value :
40776                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40777     },
40778
40779     /**
40780      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40781      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40782      * (the default format used is "m/d/y").
40783      * <br />Usage:
40784      * <pre><code>
40785 //All of these calls set the same date value (May 4, 2006)
40786
40787 //Pass a date object:
40788 var dt = new Date('5/4/06');
40789 monthField.setValue(dt);
40790
40791 //Pass a date string (default format):
40792 monthField.setValue('5/4/06');
40793
40794 //Pass a date string (custom format):
40795 monthField.format = 'Y-m-d';
40796 monthField.setValue('2006-5-4');
40797 </code></pre>
40798      * @param {String/Date} date The date or valid date string
40799      */
40800     setValue : function(date){
40801         Roo.log('month setValue' + date);
40802         // can only be first of month..
40803         
40804         var val = this.parseDate(date);
40805         
40806         if (this.hiddenField) {
40807             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40808         }
40809         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40810         this.value = this.parseDate(date);
40811     },
40812
40813     // private
40814     parseDate : function(value){
40815         if(!value || value instanceof Date){
40816             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40817             return value;
40818         }
40819         var v = Date.parseDate(value, this.format);
40820         if (!v && this.useIso) {
40821             v = Date.parseDate(value, 'Y-m-d');
40822         }
40823         if (v) {
40824             // 
40825             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40826         }
40827         
40828         
40829         if(!v && this.altFormats){
40830             if(!this.altFormatsArray){
40831                 this.altFormatsArray = this.altFormats.split("|");
40832             }
40833             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40834                 v = Date.parseDate(value, this.altFormatsArray[i]);
40835             }
40836         }
40837         return v;
40838     },
40839
40840     // private
40841     formatDate : function(date, fmt){
40842         return (!date || !(date instanceof Date)) ?
40843                date : date.dateFormat(fmt || this.format);
40844     },
40845
40846     // private
40847     menuListeners : {
40848         select: function(m, d){
40849             this.setValue(d);
40850             this.fireEvent('select', this, d);
40851         },
40852         show : function(){ // retain focus styling
40853             this.onFocus();
40854         },
40855         hide : function(){
40856             this.focus.defer(10, this);
40857             var ml = this.menuListeners;
40858             this.menu.un("select", ml.select,  this);
40859             this.menu.un("show", ml.show,  this);
40860             this.menu.un("hide", ml.hide,  this);
40861         }
40862     },
40863     // private
40864     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40865     onTriggerClick : function(){
40866         if(this.disabled){
40867             return;
40868         }
40869         if(this.menu == null){
40870             this.menu = new Roo.menu.DateMenu();
40871            
40872         }
40873         
40874         Roo.apply(this.menu.picker,  {
40875             
40876             showClear: this.allowBlank,
40877             minDate : this.minValue,
40878             maxDate : this.maxValue,
40879             disabledDatesRE : this.ddMatch,
40880             disabledDatesText : this.disabledDatesText,
40881             
40882             format : this.useIso ? 'Y-m-d' : this.format,
40883             minText : String.format(this.minText, this.formatDate(this.minValue)),
40884             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40885             
40886         });
40887          this.menu.on(Roo.apply({}, this.menuListeners, {
40888             scope:this
40889         }));
40890        
40891         
40892         var m = this.menu;
40893         var p = m.picker;
40894         
40895         // hide month picker get's called when we called by 'before hide';
40896         
40897         var ignorehide = true;
40898         p.hideMonthPicker  = function(disableAnim){
40899             if (ignorehide) {
40900                 return;
40901             }
40902              if(this.monthPicker){
40903                 Roo.log("hideMonthPicker called");
40904                 if(disableAnim === true){
40905                     this.monthPicker.hide();
40906                 }else{
40907                     this.monthPicker.slideOut('t', {duration:.2});
40908                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40909                     p.fireEvent("select", this, this.value);
40910                     m.hide();
40911                 }
40912             }
40913         }
40914         
40915         Roo.log('picker set value');
40916         Roo.log(this.getValue());
40917         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40918         m.show(this.el, 'tl-bl?');
40919         ignorehide  = false;
40920         // this will trigger hideMonthPicker..
40921         
40922         
40923         // hidden the day picker
40924         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40925         
40926         
40927         
40928       
40929         
40930         p.showMonthPicker.defer(100, p);
40931     
40932         
40933        
40934     },
40935
40936     beforeBlur : function(){
40937         var v = this.parseDate(this.getRawValue());
40938         if(v){
40939             this.setValue(v);
40940         }
40941     }
40942
40943     /** @cfg {Boolean} grow @hide */
40944     /** @cfg {Number} growMin @hide */
40945     /** @cfg {Number} growMax @hide */
40946     /**
40947      * @hide
40948      * @method autoSize
40949      */
40950 });/*
40951  * Based on:
40952  * Ext JS Library 1.1.1
40953  * Copyright(c) 2006-2007, Ext JS, LLC.
40954  *
40955  * Originally Released Under LGPL - original licence link has changed is not relivant.
40956  *
40957  * Fork - LGPL
40958  * <script type="text/javascript">
40959  */
40960  
40961
40962 /**
40963  * @class Roo.form.ComboBox
40964  * @extends Roo.form.TriggerField
40965  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40966  * @constructor
40967  * Create a new ComboBox.
40968  * @param {Object} config Configuration options
40969  */
40970 Roo.form.ComboBox = function(config){
40971     Roo.form.ComboBox.superclass.constructor.call(this, config);
40972     this.addEvents({
40973         /**
40974          * @event expand
40975          * Fires when the dropdown list is expanded
40976              * @param {Roo.form.ComboBox} combo This combo box
40977              */
40978         'expand' : true,
40979         /**
40980          * @event collapse
40981          * Fires when the dropdown list is collapsed
40982              * @param {Roo.form.ComboBox} combo This combo box
40983              */
40984         'collapse' : true,
40985         /**
40986          * @event beforeselect
40987          * Fires before a list item is selected. Return false to cancel the selection.
40988              * @param {Roo.form.ComboBox} combo This combo box
40989              * @param {Roo.data.Record} record The data record returned from the underlying store
40990              * @param {Number} index The index of the selected item in the dropdown list
40991              */
40992         'beforeselect' : true,
40993         /**
40994          * @event select
40995          * Fires when a list item is selected
40996              * @param {Roo.form.ComboBox} combo This combo box
40997              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40998              * @param {Number} index The index of the selected item in the dropdown list
40999              */
41000         'select' : true,
41001         /**
41002          * @event beforequery
41003          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41004          * The event object passed has these properties:
41005              * @param {Roo.form.ComboBox} combo This combo box
41006              * @param {String} query The query
41007              * @param {Boolean} forceAll true to force "all" query
41008              * @param {Boolean} cancel true to cancel the query
41009              * @param {Object} e The query event object
41010              */
41011         'beforequery': true,
41012          /**
41013          * @event add
41014          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41015              * @param {Roo.form.ComboBox} combo This combo box
41016              */
41017         'add' : true,
41018         /**
41019          * @event edit
41020          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41021              * @param {Roo.form.ComboBox} combo This combo box
41022              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41023              */
41024         'edit' : true
41025         
41026         
41027     });
41028     if(this.transform){
41029         this.allowDomMove = false;
41030         var s = Roo.getDom(this.transform);
41031         if(!this.hiddenName){
41032             this.hiddenName = s.name;
41033         }
41034         if(!this.store){
41035             this.mode = 'local';
41036             var d = [], opts = s.options;
41037             for(var i = 0, len = opts.length;i < len; i++){
41038                 var o = opts[i];
41039                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41040                 if(o.selected) {
41041                     this.value = value;
41042                 }
41043                 d.push([value, o.text]);
41044             }
41045             this.store = new Roo.data.SimpleStore({
41046                 'id': 0,
41047                 fields: ['value', 'text'],
41048                 data : d
41049             });
41050             this.valueField = 'value';
41051             this.displayField = 'text';
41052         }
41053         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41054         if(!this.lazyRender){
41055             this.target = true;
41056             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41057             s.parentNode.removeChild(s); // remove it
41058             this.render(this.el.parentNode);
41059         }else{
41060             s.parentNode.removeChild(s); // remove it
41061         }
41062
41063     }
41064     if (this.store) {
41065         this.store = Roo.factory(this.store, Roo.data);
41066     }
41067     
41068     this.selectedIndex = -1;
41069     if(this.mode == 'local'){
41070         if(config.queryDelay === undefined){
41071             this.queryDelay = 10;
41072         }
41073         if(config.minChars === undefined){
41074             this.minChars = 0;
41075         }
41076     }
41077 };
41078
41079 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41080     /**
41081      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41082      */
41083     /**
41084      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41085      * rendering into an Roo.Editor, defaults to false)
41086      */
41087     /**
41088      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41089      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41090      */
41091     /**
41092      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41093      */
41094     /**
41095      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41096      * the dropdown list (defaults to undefined, with no header element)
41097      */
41098
41099      /**
41100      * @cfg {String/Roo.Template} tpl The template to use to render the output
41101      */
41102      
41103     // private
41104     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41105     /**
41106      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41107      */
41108     listWidth: undefined,
41109     /**
41110      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41111      * mode = 'remote' or 'text' if mode = 'local')
41112      */
41113     displayField: undefined,
41114     /**
41115      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41116      * mode = 'remote' or 'value' if mode = 'local'). 
41117      * Note: use of a valueField requires the user make a selection
41118      * in order for a value to be mapped.
41119      */
41120     valueField: undefined,
41121     
41122     
41123     /**
41124      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41125      * field's data value (defaults to the underlying DOM element's name)
41126      */
41127     hiddenName: undefined,
41128     /**
41129      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41130      */
41131     listClass: '',
41132     /**
41133      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41134      */
41135     selectedClass: 'x-combo-selected',
41136     /**
41137      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41138      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41139      * which displays a downward arrow icon).
41140      */
41141     triggerClass : 'x-form-arrow-trigger',
41142     /**
41143      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41144      */
41145     shadow:'sides',
41146     /**
41147      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41148      * anchor positions (defaults to 'tl-bl')
41149      */
41150     listAlign: 'tl-bl?',
41151     /**
41152      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41153      */
41154     maxHeight: 300,
41155     /**
41156      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41157      * query specified by the allQuery config option (defaults to 'query')
41158      */
41159     triggerAction: 'query',
41160     /**
41161      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41162      * (defaults to 4, does not apply if editable = false)
41163      */
41164     minChars : 4,
41165     /**
41166      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41167      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41168      */
41169     typeAhead: false,
41170     /**
41171      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41172      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41173      */
41174     queryDelay: 500,
41175     /**
41176      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41177      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41178      */
41179     pageSize: 0,
41180     /**
41181      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41182      * when editable = true (defaults to false)
41183      */
41184     selectOnFocus:false,
41185     /**
41186      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41187      */
41188     queryParam: 'query',
41189     /**
41190      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41191      * when mode = 'remote' (defaults to 'Loading...')
41192      */
41193     loadingText: 'Loading...',
41194     /**
41195      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41196      */
41197     resizable: false,
41198     /**
41199      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41200      */
41201     handleHeight : 8,
41202     /**
41203      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41204      * traditional select (defaults to true)
41205      */
41206     editable: true,
41207     /**
41208      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41209      */
41210     allQuery: '',
41211     /**
41212      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41213      */
41214     mode: 'remote',
41215     /**
41216      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41217      * listWidth has a higher value)
41218      */
41219     minListWidth : 70,
41220     /**
41221      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41222      * allow the user to set arbitrary text into the field (defaults to false)
41223      */
41224     forceSelection:false,
41225     /**
41226      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41227      * if typeAhead = true (defaults to 250)
41228      */
41229     typeAheadDelay : 250,
41230     /**
41231      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41232      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41233      */
41234     valueNotFoundText : undefined,
41235     /**
41236      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41237      */
41238     blockFocus : false,
41239     
41240     /**
41241      * @cfg {Boolean} disableClear Disable showing of clear button.
41242      */
41243     disableClear : false,
41244     /**
41245      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41246      */
41247     alwaysQuery : false,
41248     
41249     //private
41250     addicon : false,
41251     editicon: false,
41252     
41253     // element that contains real text value.. (when hidden is used..)
41254      
41255     // private
41256     onRender : function(ct, position){
41257         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41258         if(this.hiddenName){
41259             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41260                     'before', true);
41261             this.hiddenField.value =
41262                 this.hiddenValue !== undefined ? this.hiddenValue :
41263                 this.value !== undefined ? this.value : '';
41264
41265             // prevent input submission
41266             this.el.dom.removeAttribute('name');
41267              
41268              
41269         }
41270         if(Roo.isGecko){
41271             this.el.dom.setAttribute('autocomplete', 'off');
41272         }
41273
41274         var cls = 'x-combo-list';
41275
41276         this.list = new Roo.Layer({
41277             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41278         });
41279
41280         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41281         this.list.setWidth(lw);
41282         this.list.swallowEvent('mousewheel');
41283         this.assetHeight = 0;
41284
41285         if(this.title){
41286             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41287             this.assetHeight += this.header.getHeight();
41288         }
41289
41290         this.innerList = this.list.createChild({cls:cls+'-inner'});
41291         this.innerList.on('mouseover', this.onViewOver, this);
41292         this.innerList.on('mousemove', this.onViewMove, this);
41293         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41294         
41295         if(this.allowBlank && !this.pageSize && !this.disableClear){
41296             this.footer = this.list.createChild({cls:cls+'-ft'});
41297             this.pageTb = new Roo.Toolbar(this.footer);
41298            
41299         }
41300         if(this.pageSize){
41301             this.footer = this.list.createChild({cls:cls+'-ft'});
41302             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41303                     {pageSize: this.pageSize});
41304             
41305         }
41306         
41307         if (this.pageTb && this.allowBlank && !this.disableClear) {
41308             var _this = this;
41309             this.pageTb.add(new Roo.Toolbar.Fill(), {
41310                 cls: 'x-btn-icon x-btn-clear',
41311                 text: '&#160;',
41312                 handler: function()
41313                 {
41314                     _this.collapse();
41315                     _this.clearValue();
41316                     _this.onSelect(false, -1);
41317                 }
41318             });
41319         }
41320         if (this.footer) {
41321             this.assetHeight += this.footer.getHeight();
41322         }
41323         
41324
41325         if(!this.tpl){
41326             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41327         }
41328
41329         this.view = new Roo.View(this.innerList, this.tpl, {
41330             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41331         });
41332
41333         this.view.on('click', this.onViewClick, this);
41334
41335         this.store.on('beforeload', this.onBeforeLoad, this);
41336         this.store.on('load', this.onLoad, this);
41337         this.store.on('loadexception', this.onLoadException, this);
41338
41339         if(this.resizable){
41340             this.resizer = new Roo.Resizable(this.list,  {
41341                pinned:true, handles:'se'
41342             });
41343             this.resizer.on('resize', function(r, w, h){
41344                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41345                 this.listWidth = w;
41346                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41347                 this.restrictHeight();
41348             }, this);
41349             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41350         }
41351         if(!this.editable){
41352             this.editable = true;
41353             this.setEditable(false);
41354         }  
41355         
41356         
41357         if (typeof(this.events.add.listeners) != 'undefined') {
41358             
41359             this.addicon = this.wrap.createChild(
41360                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41361        
41362             this.addicon.on('click', function(e) {
41363                 this.fireEvent('add', this);
41364             }, this);
41365         }
41366         if (typeof(this.events.edit.listeners) != 'undefined') {
41367             
41368             this.editicon = this.wrap.createChild(
41369                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41370             if (this.addicon) {
41371                 this.editicon.setStyle('margin-left', '40px');
41372             }
41373             this.editicon.on('click', function(e) {
41374                 
41375                 // we fire even  if inothing is selected..
41376                 this.fireEvent('edit', this, this.lastData );
41377                 
41378             }, this);
41379         }
41380         
41381         
41382         
41383     },
41384
41385     // private
41386     initEvents : function(){
41387         Roo.form.ComboBox.superclass.initEvents.call(this);
41388
41389         this.keyNav = new Roo.KeyNav(this.el, {
41390             "up" : function(e){
41391                 this.inKeyMode = true;
41392                 this.selectPrev();
41393             },
41394
41395             "down" : function(e){
41396                 if(!this.isExpanded()){
41397                     this.onTriggerClick();
41398                 }else{
41399                     this.inKeyMode = true;
41400                     this.selectNext();
41401                 }
41402             },
41403
41404             "enter" : function(e){
41405                 this.onViewClick();
41406                 //return true;
41407             },
41408
41409             "esc" : function(e){
41410                 this.collapse();
41411             },
41412
41413             "tab" : function(e){
41414                 this.onViewClick(false);
41415                 this.fireEvent("specialkey", this, e);
41416                 return true;
41417             },
41418
41419             scope : this,
41420
41421             doRelay : function(foo, bar, hname){
41422                 if(hname == 'down' || this.scope.isExpanded()){
41423                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41424                 }
41425                 return true;
41426             },
41427
41428             forceKeyDown: true
41429         });
41430         this.queryDelay = Math.max(this.queryDelay || 10,
41431                 this.mode == 'local' ? 10 : 250);
41432         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41433         if(this.typeAhead){
41434             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41435         }
41436         if(this.editable !== false){
41437             this.el.on("keyup", this.onKeyUp, this);
41438         }
41439         if(this.forceSelection){
41440             this.on('blur', this.doForce, this);
41441         }
41442     },
41443
41444     onDestroy : function(){
41445         if(this.view){
41446             this.view.setStore(null);
41447             this.view.el.removeAllListeners();
41448             this.view.el.remove();
41449             this.view.purgeListeners();
41450         }
41451         if(this.list){
41452             this.list.destroy();
41453         }
41454         if(this.store){
41455             this.store.un('beforeload', this.onBeforeLoad, this);
41456             this.store.un('load', this.onLoad, this);
41457             this.store.un('loadexception', this.onLoadException, this);
41458         }
41459         Roo.form.ComboBox.superclass.onDestroy.call(this);
41460     },
41461
41462     // private
41463     fireKey : function(e){
41464         if(e.isNavKeyPress() && !this.list.isVisible()){
41465             this.fireEvent("specialkey", this, e);
41466         }
41467     },
41468
41469     // private
41470     onResize: function(w, h){
41471         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41472         
41473         if(typeof w != 'number'){
41474             // we do not handle it!?!?
41475             return;
41476         }
41477         var tw = this.trigger.getWidth();
41478         tw += this.addicon ? this.addicon.getWidth() : 0;
41479         tw += this.editicon ? this.editicon.getWidth() : 0;
41480         var x = w - tw;
41481         this.el.setWidth( this.adjustWidth('input', x));
41482             
41483         this.trigger.setStyle('left', x+'px');
41484         
41485         if(this.list && this.listWidth === undefined){
41486             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41487             this.list.setWidth(lw);
41488             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41489         }
41490         
41491     
41492         
41493     },
41494
41495     /**
41496      * Allow or prevent the user from directly editing the field text.  If false is passed,
41497      * the user will only be able to select from the items defined in the dropdown list.  This method
41498      * is the runtime equivalent of setting the 'editable' config option at config time.
41499      * @param {Boolean} value True to allow the user to directly edit the field text
41500      */
41501     setEditable : function(value){
41502         if(value == this.editable){
41503             return;
41504         }
41505         this.editable = value;
41506         if(!value){
41507             this.el.dom.setAttribute('readOnly', true);
41508             this.el.on('mousedown', this.onTriggerClick,  this);
41509             this.el.addClass('x-combo-noedit');
41510         }else{
41511             this.el.dom.setAttribute('readOnly', false);
41512             this.el.un('mousedown', this.onTriggerClick,  this);
41513             this.el.removeClass('x-combo-noedit');
41514         }
41515     },
41516
41517     // private
41518     onBeforeLoad : function(){
41519         if(!this.hasFocus){
41520             return;
41521         }
41522         this.innerList.update(this.loadingText ?
41523                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41524         this.restrictHeight();
41525         this.selectedIndex = -1;
41526     },
41527
41528     // private
41529     onLoad : function(){
41530         if(!this.hasFocus){
41531             return;
41532         }
41533         if(this.store.getCount() > 0){
41534             this.expand();
41535             this.restrictHeight();
41536             if(this.lastQuery == this.allQuery){
41537                 if(this.editable){
41538                     this.el.dom.select();
41539                 }
41540                 if(!this.selectByValue(this.value, true)){
41541                     this.select(0, true);
41542                 }
41543             }else{
41544                 this.selectNext();
41545                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41546                     this.taTask.delay(this.typeAheadDelay);
41547                 }
41548             }
41549         }else{
41550             this.onEmptyResults();
41551         }
41552         //this.el.focus();
41553     },
41554     // private
41555     onLoadException : function()
41556     {
41557         this.collapse();
41558         Roo.log(this.store.reader.jsonData);
41559         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41560             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41561         }
41562         
41563         
41564     },
41565     // private
41566     onTypeAhead : function(){
41567         if(this.store.getCount() > 0){
41568             var r = this.store.getAt(0);
41569             var newValue = r.data[this.displayField];
41570             var len = newValue.length;
41571             var selStart = this.getRawValue().length;
41572             if(selStart != len){
41573                 this.setRawValue(newValue);
41574                 this.selectText(selStart, newValue.length);
41575             }
41576         }
41577     },
41578
41579     // private
41580     onSelect : function(record, index){
41581         if(this.fireEvent('beforeselect', this, record, index) !== false){
41582             this.setFromData(index > -1 ? record.data : false);
41583             this.collapse();
41584             this.fireEvent('select', this, record, index);
41585         }
41586     },
41587
41588     /**
41589      * Returns the currently selected field value or empty string if no value is set.
41590      * @return {String} value The selected value
41591      */
41592     getValue : function(){
41593         if(this.valueField){
41594             return typeof this.value != 'undefined' ? this.value : '';
41595         }
41596         return Roo.form.ComboBox.superclass.getValue.call(this);
41597     },
41598
41599     /**
41600      * Clears any text/value currently set in the field
41601      */
41602     clearValue : function(){
41603         if(this.hiddenField){
41604             this.hiddenField.value = '';
41605         }
41606         this.value = '';
41607         this.setRawValue('');
41608         this.lastSelectionText = '';
41609         
41610     },
41611
41612     /**
41613      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41614      * will be displayed in the field.  If the value does not match the data value of an existing item,
41615      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41616      * Otherwise the field will be blank (although the value will still be set).
41617      * @param {String} value The value to match
41618      */
41619     setValue : function(v){
41620         var text = v;
41621         if(this.valueField){
41622             var r = this.findRecord(this.valueField, v);
41623             if(r){
41624                 text = r.data[this.displayField];
41625             }else if(this.valueNotFoundText !== undefined){
41626                 text = this.valueNotFoundText;
41627             }
41628         }
41629         this.lastSelectionText = text;
41630         if(this.hiddenField){
41631             this.hiddenField.value = v;
41632         }
41633         Roo.form.ComboBox.superclass.setValue.call(this, text);
41634         this.value = v;
41635     },
41636     /**
41637      * @property {Object} the last set data for the element
41638      */
41639     
41640     lastData : false,
41641     /**
41642      * Sets the value of the field based on a object which is related to the record format for the store.
41643      * @param {Object} value the value to set as. or false on reset?
41644      */
41645     setFromData : function(o){
41646         var dv = ''; // display value
41647         var vv = ''; // value value..
41648         this.lastData = o;
41649         if (this.displayField) {
41650             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41651         } else {
41652             // this is an error condition!!!
41653             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41654         }
41655         
41656         if(this.valueField){
41657             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41658         }
41659         if(this.hiddenField){
41660             this.hiddenField.value = vv;
41661             
41662             this.lastSelectionText = dv;
41663             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41664             this.value = vv;
41665             return;
41666         }
41667         // no hidden field.. - we store the value in 'value', but still display
41668         // display field!!!!
41669         this.lastSelectionText = dv;
41670         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41671         this.value = vv;
41672         
41673         
41674     },
41675     // private
41676     reset : function(){
41677         // overridden so that last data is reset..
41678         this.setValue(this.resetValue);
41679         this.originalValue = this.getValue();
41680         this.clearInvalid();
41681         this.lastData = false;
41682         if (this.view) {
41683             this.view.clearSelections();
41684         }
41685     },
41686     // private
41687     findRecord : function(prop, value){
41688         var record;
41689         if(this.store.getCount() > 0){
41690             this.store.each(function(r){
41691                 if(r.data[prop] == value){
41692                     record = r;
41693                     return false;
41694                 }
41695                 return true;
41696             });
41697         }
41698         return record;
41699     },
41700     
41701     getName: function()
41702     {
41703         // returns hidden if it's set..
41704         if (!this.rendered) {return ''};
41705         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41706         
41707     },
41708     // private
41709     onViewMove : function(e, t){
41710         this.inKeyMode = false;
41711     },
41712
41713     // private
41714     onViewOver : function(e, t){
41715         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41716             return;
41717         }
41718         var item = this.view.findItemFromChild(t);
41719         if(item){
41720             var index = this.view.indexOf(item);
41721             this.select(index, false);
41722         }
41723     },
41724
41725     // private
41726     onViewClick : function(doFocus)
41727     {
41728         var index = this.view.getSelectedIndexes()[0];
41729         var r = this.store.getAt(index);
41730         if(r){
41731             this.onSelect(r, index);
41732         }
41733         if(doFocus !== false && !this.blockFocus){
41734             this.el.focus();
41735         }
41736     },
41737
41738     // private
41739     restrictHeight : function(){
41740         this.innerList.dom.style.height = '';
41741         var inner = this.innerList.dom;
41742         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41743         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41744         this.list.beginUpdate();
41745         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41746         this.list.alignTo(this.el, this.listAlign);
41747         this.list.endUpdate();
41748     },
41749
41750     // private
41751     onEmptyResults : function(){
41752         this.collapse();
41753     },
41754
41755     /**
41756      * Returns true if the dropdown list is expanded, else false.
41757      */
41758     isExpanded : function(){
41759         return this.list.isVisible();
41760     },
41761
41762     /**
41763      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41764      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41765      * @param {String} value The data value of the item to select
41766      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41767      * selected item if it is not currently in view (defaults to true)
41768      * @return {Boolean} True if the value matched an item in the list, else false
41769      */
41770     selectByValue : function(v, scrollIntoView){
41771         if(v !== undefined && v !== null){
41772             var r = this.findRecord(this.valueField || this.displayField, v);
41773             if(r){
41774                 this.select(this.store.indexOf(r), scrollIntoView);
41775                 return true;
41776             }
41777         }
41778         return false;
41779     },
41780
41781     /**
41782      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41783      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41784      * @param {Number} index The zero-based index of the list item to select
41785      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41786      * selected item if it is not currently in view (defaults to true)
41787      */
41788     select : function(index, scrollIntoView){
41789         this.selectedIndex = index;
41790         this.view.select(index);
41791         if(scrollIntoView !== false){
41792             var el = this.view.getNode(index);
41793             if(el){
41794                 this.innerList.scrollChildIntoView(el, false);
41795             }
41796         }
41797     },
41798
41799     // private
41800     selectNext : function(){
41801         var ct = this.store.getCount();
41802         if(ct > 0){
41803             if(this.selectedIndex == -1){
41804                 this.select(0);
41805             }else if(this.selectedIndex < ct-1){
41806                 this.select(this.selectedIndex+1);
41807             }
41808         }
41809     },
41810
41811     // private
41812     selectPrev : function(){
41813         var ct = this.store.getCount();
41814         if(ct > 0){
41815             if(this.selectedIndex == -1){
41816                 this.select(0);
41817             }else if(this.selectedIndex != 0){
41818                 this.select(this.selectedIndex-1);
41819             }
41820         }
41821     },
41822
41823     // private
41824     onKeyUp : function(e){
41825         if(this.editable !== false && !e.isSpecialKey()){
41826             this.lastKey = e.getKey();
41827             this.dqTask.delay(this.queryDelay);
41828         }
41829     },
41830
41831     // private
41832     validateBlur : function(){
41833         return !this.list || !this.list.isVisible();   
41834     },
41835
41836     // private
41837     initQuery : function(){
41838         this.doQuery(this.getRawValue());
41839     },
41840
41841     // private
41842     doForce : function(){
41843         if(this.el.dom.value.length > 0){
41844             this.el.dom.value =
41845                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41846              
41847         }
41848     },
41849
41850     /**
41851      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41852      * query allowing the query action to be canceled if needed.
41853      * @param {String} query The SQL query to execute
41854      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41855      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41856      * saved in the current store (defaults to false)
41857      */
41858     doQuery : function(q, forceAll){
41859         if(q === undefined || q === null){
41860             q = '';
41861         }
41862         var qe = {
41863             query: q,
41864             forceAll: forceAll,
41865             combo: this,
41866             cancel:false
41867         };
41868         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41869             return false;
41870         }
41871         q = qe.query;
41872         forceAll = qe.forceAll;
41873         if(forceAll === true || (q.length >= this.minChars)){
41874             if(this.lastQuery != q || this.alwaysQuery){
41875                 this.lastQuery = q;
41876                 if(this.mode == 'local'){
41877                     this.selectedIndex = -1;
41878                     if(forceAll){
41879                         this.store.clearFilter();
41880                     }else{
41881                         this.store.filter(this.displayField, q);
41882                     }
41883                     this.onLoad();
41884                 }else{
41885                     this.store.baseParams[this.queryParam] = q;
41886                     this.store.load({
41887                         params: this.getParams(q)
41888                     });
41889                     this.expand();
41890                 }
41891             }else{
41892                 this.selectedIndex = -1;
41893                 this.onLoad();   
41894             }
41895         }
41896     },
41897
41898     // private
41899     getParams : function(q){
41900         var p = {};
41901         //p[this.queryParam] = q;
41902         if(this.pageSize){
41903             p.start = 0;
41904             p.limit = this.pageSize;
41905         }
41906         return p;
41907     },
41908
41909     /**
41910      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41911      */
41912     collapse : function(){
41913         if(!this.isExpanded()){
41914             return;
41915         }
41916         this.list.hide();
41917         Roo.get(document).un('mousedown', this.collapseIf, this);
41918         Roo.get(document).un('mousewheel', this.collapseIf, this);
41919         if (!this.editable) {
41920             Roo.get(document).un('keydown', this.listKeyPress, this);
41921         }
41922         this.fireEvent('collapse', this);
41923     },
41924
41925     // private
41926     collapseIf : function(e){
41927         if(!e.within(this.wrap) && !e.within(this.list)){
41928             this.collapse();
41929         }
41930     },
41931
41932     /**
41933      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41934      */
41935     expand : function(){
41936         if(this.isExpanded() || !this.hasFocus){
41937             return;
41938         }
41939         this.list.alignTo(this.el, this.listAlign);
41940         this.list.show();
41941         Roo.get(document).on('mousedown', this.collapseIf, this);
41942         Roo.get(document).on('mousewheel', this.collapseIf, this);
41943         if (!this.editable) {
41944             Roo.get(document).on('keydown', this.listKeyPress, this);
41945         }
41946         
41947         this.fireEvent('expand', this);
41948     },
41949
41950     // private
41951     // Implements the default empty TriggerField.onTriggerClick function
41952     onTriggerClick : function(){
41953         if(this.disabled){
41954             return;
41955         }
41956         if(this.isExpanded()){
41957             this.collapse();
41958             if (!this.blockFocus) {
41959                 this.el.focus();
41960             }
41961             
41962         }else {
41963             this.hasFocus = true;
41964             if(this.triggerAction == 'all') {
41965                 this.doQuery(this.allQuery, true);
41966             } else {
41967                 this.doQuery(this.getRawValue());
41968             }
41969             if (!this.blockFocus) {
41970                 this.el.focus();
41971             }
41972         }
41973     },
41974     listKeyPress : function(e)
41975     {
41976         //Roo.log('listkeypress');
41977         // scroll to first matching element based on key pres..
41978         if (e.isSpecialKey()) {
41979             return false;
41980         }
41981         var k = String.fromCharCode(e.getKey()).toUpperCase();
41982         //Roo.log(k);
41983         var match  = false;
41984         var csel = this.view.getSelectedNodes();
41985         var cselitem = false;
41986         if (csel.length) {
41987             var ix = this.view.indexOf(csel[0]);
41988             cselitem  = this.store.getAt(ix);
41989             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41990                 cselitem = false;
41991             }
41992             
41993         }
41994         
41995         this.store.each(function(v) { 
41996             if (cselitem) {
41997                 // start at existing selection.
41998                 if (cselitem.id == v.id) {
41999                     cselitem = false;
42000                 }
42001                 return;
42002             }
42003                 
42004             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42005                 match = this.store.indexOf(v);
42006                 return false;
42007             }
42008         }, this);
42009         
42010         if (match === false) {
42011             return true; // no more action?
42012         }
42013         // scroll to?
42014         this.view.select(match);
42015         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42016         sn.scrollIntoView(sn.dom.parentNode, false);
42017     }
42018
42019     /** 
42020     * @cfg {Boolean} grow 
42021     * @hide 
42022     */
42023     /** 
42024     * @cfg {Number} growMin 
42025     * @hide 
42026     */
42027     /** 
42028     * @cfg {Number} growMax 
42029     * @hide 
42030     */
42031     /**
42032      * @hide
42033      * @method autoSize
42034      */
42035 });/*
42036  * Copyright(c) 2010-2012, Roo J Solutions Limited
42037  *
42038  * Licence LGPL
42039  *
42040  */
42041
42042 /**
42043  * @class Roo.form.ComboBoxArray
42044  * @extends Roo.form.TextField
42045  * A facebook style adder... for lists of email / people / countries  etc...
42046  * pick multiple items from a combo box, and shows each one.
42047  *
42048  *  Fred [x]  Brian [x]  [Pick another |v]
42049  *
42050  *
42051  *  For this to work: it needs various extra information
42052  *    - normal combo problay has
42053  *      name, hiddenName
42054  *    + displayField, valueField
42055  *
42056  *    For our purpose...
42057  *
42058  *
42059  *   If we change from 'extends' to wrapping...
42060  *   
42061  *  
42062  *
42063  
42064  
42065  * @constructor
42066  * Create a new ComboBoxArray.
42067  * @param {Object} config Configuration options
42068  */
42069  
42070
42071 Roo.form.ComboBoxArray = function(config)
42072 {
42073     this.addEvents({
42074         /**
42075          * @event beforeremove
42076          * Fires before remove the value from the list
42077              * @param {Roo.form.ComboBoxArray} _self This combo box array
42078              * @param {Roo.form.ComboBoxArray.Item} item removed item
42079              */
42080         'beforeremove' : true,
42081         /**
42082          * @event remove
42083          * Fires when remove the value from the list
42084              * @param {Roo.form.ComboBoxArray} _self This combo box array
42085              * @param {Roo.form.ComboBoxArray.Item} item removed item
42086              */
42087         'remove' : true
42088         
42089         
42090     });
42091     
42092     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42093     
42094     this.items = new Roo.util.MixedCollection(false);
42095     
42096     // construct the child combo...
42097     
42098     
42099     
42100     
42101    
42102     
42103 }
42104
42105  
42106 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42107
42108     /**
42109      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42110      */
42111     
42112     lastData : false,
42113     
42114     // behavies liek a hiddne field
42115     inputType:      'hidden',
42116     /**
42117      * @cfg {Number} width The width of the box that displays the selected element
42118      */ 
42119     width:          300,
42120
42121     
42122     
42123     /**
42124      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42125      */
42126     name : false,
42127     /**
42128      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42129      */
42130     hiddenName : false,
42131     
42132     
42133     // private the array of items that are displayed..
42134     items  : false,
42135     // private - the hidden field el.
42136     hiddenEl : false,
42137     // private - the filed el..
42138     el : false,
42139     
42140     //validateValue : function() { return true; }, // all values are ok!
42141     //onAddClick: function() { },
42142     
42143     onRender : function(ct, position) 
42144     {
42145         
42146         // create the standard hidden element
42147         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42148         
42149         
42150         // give fake names to child combo;
42151         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42152         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42153         
42154         this.combo = Roo.factory(this.combo, Roo.form);
42155         this.combo.onRender(ct, position);
42156         if (typeof(this.combo.width) != 'undefined') {
42157             this.combo.onResize(this.combo.width,0);
42158         }
42159         
42160         this.combo.initEvents();
42161         
42162         // assigned so form know we need to do this..
42163         this.store          = this.combo.store;
42164         this.valueField     = this.combo.valueField;
42165         this.displayField   = this.combo.displayField ;
42166         
42167         
42168         this.combo.wrap.addClass('x-cbarray-grp');
42169         
42170         var cbwrap = this.combo.wrap.createChild(
42171             {tag: 'div', cls: 'x-cbarray-cb'},
42172             this.combo.el.dom
42173         );
42174         
42175              
42176         this.hiddenEl = this.combo.wrap.createChild({
42177             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42178         });
42179         this.el = this.combo.wrap.createChild({
42180             tag: 'input',  type:'hidden' , name: this.name, value : ''
42181         });
42182          //   this.el.dom.removeAttribute("name");
42183         
42184         
42185         this.outerWrap = this.combo.wrap;
42186         this.wrap = cbwrap;
42187         
42188         this.outerWrap.setWidth(this.width);
42189         this.outerWrap.dom.removeChild(this.el.dom);
42190         
42191         this.wrap.dom.appendChild(this.el.dom);
42192         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42193         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42194         
42195         this.combo.trigger.setStyle('position','relative');
42196         this.combo.trigger.setStyle('left', '0px');
42197         this.combo.trigger.setStyle('top', '2px');
42198         
42199         this.combo.el.setStyle('vertical-align', 'text-bottom');
42200         
42201         //this.trigger.setStyle('vertical-align', 'top');
42202         
42203         // this should use the code from combo really... on('add' ....)
42204         if (this.adder) {
42205             
42206         
42207             this.adder = this.outerWrap.createChild(
42208                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42209             var _t = this;
42210             this.adder.on('click', function(e) {
42211                 _t.fireEvent('adderclick', this, e);
42212             }, _t);
42213         }
42214         //var _t = this;
42215         //this.adder.on('click', this.onAddClick, _t);
42216         
42217         
42218         this.combo.on('select', function(cb, rec, ix) {
42219             this.addItem(rec.data);
42220             
42221             cb.setValue('');
42222             cb.el.dom.value = '';
42223             //cb.lastData = rec.data;
42224             // add to list
42225             
42226         }, this);
42227         
42228         
42229     },
42230     
42231     
42232     getName: function()
42233     {
42234         // returns hidden if it's set..
42235         if (!this.rendered) {return ''};
42236         return  this.hiddenName ? this.hiddenName : this.name;
42237         
42238     },
42239     
42240     
42241     onResize: function(w, h){
42242         
42243         return;
42244         // not sure if this is needed..
42245         //this.combo.onResize(w,h);
42246         
42247         if(typeof w != 'number'){
42248             // we do not handle it!?!?
42249             return;
42250         }
42251         var tw = this.combo.trigger.getWidth();
42252         tw += this.addicon ? this.addicon.getWidth() : 0;
42253         tw += this.editicon ? this.editicon.getWidth() : 0;
42254         var x = w - tw;
42255         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42256             
42257         this.combo.trigger.setStyle('left', '0px');
42258         
42259         if(this.list && this.listWidth === undefined){
42260             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42261             this.list.setWidth(lw);
42262             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42263         }
42264         
42265     
42266         
42267     },
42268     
42269     addItem: function(rec)
42270     {
42271         var valueField = this.combo.valueField;
42272         var displayField = this.combo.displayField;
42273         if (this.items.indexOfKey(rec[valueField]) > -1) {
42274             //console.log("GOT " + rec.data.id);
42275             return;
42276         }
42277         
42278         var x = new Roo.form.ComboBoxArray.Item({
42279             //id : rec[this.idField],
42280             data : rec,
42281             displayField : displayField ,
42282             tipField : displayField ,
42283             cb : this
42284         });
42285         // use the 
42286         this.items.add(rec[valueField],x);
42287         // add it before the element..
42288         this.updateHiddenEl();
42289         x.render(this.outerWrap, this.wrap.dom);
42290         // add the image handler..
42291     },
42292     
42293     updateHiddenEl : function()
42294     {
42295         this.validate();
42296         if (!this.hiddenEl) {
42297             return;
42298         }
42299         var ar = [];
42300         var idField = this.combo.valueField;
42301         
42302         this.items.each(function(f) {
42303             ar.push(f.data[idField]);
42304            
42305         });
42306         this.hiddenEl.dom.value = ar.join(',');
42307         this.validate();
42308     },
42309     
42310     reset : function()
42311     {
42312         this.items.clear();
42313         
42314         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42315            el.remove();
42316         });
42317         
42318         this.el.dom.value = '';
42319         if (this.hiddenEl) {
42320             this.hiddenEl.dom.value = '';
42321         }
42322         
42323     },
42324     getValue: function()
42325     {
42326         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42327     },
42328     setValue: function(v) // not a valid action - must use addItems..
42329     {
42330          
42331         this.reset();
42332         
42333         
42334         
42335         if (this.store.isLocal && (typeof(v) == 'string')) {
42336             // then we can use the store to find the values..
42337             // comma seperated at present.. this needs to allow JSON based encoding..
42338             this.hiddenEl.value  = v;
42339             var v_ar = [];
42340             Roo.each(v.split(','), function(k) {
42341                 Roo.log("CHECK " + this.valueField + ',' + k);
42342                 var li = this.store.query(this.valueField, k);
42343                 if (!li.length) {
42344                     return;
42345                 }
42346                 var add = {};
42347                 add[this.valueField] = k;
42348                 add[this.displayField] = li.item(0).data[this.displayField];
42349                 
42350                 this.addItem(add);
42351             }, this) 
42352              
42353         }
42354         if (typeof(v) == 'object' ) {
42355             // then let's assume it's an array of objects..
42356             Roo.each(v, function(l) {
42357                 this.addItem(l);
42358             }, this);
42359              
42360         }
42361         
42362         
42363     },
42364     setFromData: function(v)
42365     {
42366         // this recieves an object, if setValues is called.
42367         this.reset();
42368         this.el.dom.value = v[this.displayField];
42369         this.hiddenEl.dom.value = v[this.valueField];
42370         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42371             return;
42372         }
42373         var kv = v[this.valueField];
42374         var dv = v[this.displayField];
42375         kv = typeof(kv) != 'string' ? '' : kv;
42376         dv = typeof(dv) != 'string' ? '' : dv;
42377         
42378         
42379         var keys = kv.split(',');
42380         var display = dv.split(',');
42381         for (var i = 0 ; i < keys.length; i++) {
42382             
42383             add = {};
42384             add[this.valueField] = keys[i];
42385             add[this.displayField] = display[i];
42386             this.addItem(add);
42387         }
42388       
42389         
42390     },
42391     
42392     /**
42393      * Validates the combox array value
42394      * @return {Boolean} True if the value is valid, else false
42395      */
42396     validate : function(){
42397         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42398             this.clearInvalid();
42399             return true;
42400         }
42401         return false;
42402     },
42403     
42404     validateValue : function(value){
42405         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42406         
42407     },
42408     
42409     /*@
42410      * overide
42411      * 
42412      */
42413     isDirty : function() {
42414         if(this.disabled) {
42415             return false;
42416         }
42417         
42418         try {
42419             var d = Roo.decode(String(this.originalValue));
42420         } catch (e) {
42421             return String(this.getValue()) !== String(this.originalValue);
42422         }
42423         
42424         var originalValue = [];
42425         
42426         for (var i = 0; i < d.length; i++){
42427             originalValue.push(d[i][this.valueField]);
42428         }
42429         
42430         return String(this.getValue()) !== String(originalValue.join(','));
42431         
42432     }
42433     
42434 });
42435
42436
42437
42438 /**
42439  * @class Roo.form.ComboBoxArray.Item
42440  * @extends Roo.BoxComponent
42441  * A selected item in the list
42442  *  Fred [x]  Brian [x]  [Pick another |v]
42443  * 
42444  * @constructor
42445  * Create a new item.
42446  * @param {Object} config Configuration options
42447  */
42448  
42449 Roo.form.ComboBoxArray.Item = function(config) {
42450     config.id = Roo.id();
42451     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42452 }
42453
42454 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42455     data : {},
42456     cb: false,
42457     displayField : false,
42458     tipField : false,
42459     
42460     
42461     defaultAutoCreate : {
42462         tag: 'div',
42463         cls: 'x-cbarray-item',
42464         cn : [ 
42465             { tag: 'div' },
42466             {
42467                 tag: 'img',
42468                 width:16,
42469                 height : 16,
42470                 src : Roo.BLANK_IMAGE_URL ,
42471                 align: 'center'
42472             }
42473         ]
42474         
42475     },
42476     
42477  
42478     onRender : function(ct, position)
42479     {
42480         Roo.form.Field.superclass.onRender.call(this, ct, position);
42481         
42482         if(!this.el){
42483             var cfg = this.getAutoCreate();
42484             this.el = ct.createChild(cfg, position);
42485         }
42486         
42487         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42488         
42489         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42490             this.cb.renderer(this.data) :
42491             String.format('{0}',this.data[this.displayField]);
42492         
42493             
42494         this.el.child('div').dom.setAttribute('qtip',
42495                         String.format('{0}',this.data[this.tipField])
42496         );
42497         
42498         this.el.child('img').on('click', this.remove, this);
42499         
42500     },
42501    
42502     remove : function()
42503     {
42504         if(this.cb.disabled){
42505             return;
42506         }
42507         
42508         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42509             this.cb.items.remove(this);
42510             this.el.child('img').un('click', this.remove, this);
42511             this.el.remove();
42512             this.cb.updateHiddenEl();
42513
42514             this.cb.fireEvent('remove', this.cb, this);
42515         }
42516         
42517     }
42518 });/*
42519  * Based on:
42520  * Ext JS Library 1.1.1
42521  * Copyright(c) 2006-2007, Ext JS, LLC.
42522  *
42523  * Originally Released Under LGPL - original licence link has changed is not relivant.
42524  *
42525  * Fork - LGPL
42526  * <script type="text/javascript">
42527  */
42528 /**
42529  * @class Roo.form.Checkbox
42530  * @extends Roo.form.Field
42531  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42532  * @constructor
42533  * Creates a new Checkbox
42534  * @param {Object} config Configuration options
42535  */
42536 Roo.form.Checkbox = function(config){
42537     Roo.form.Checkbox.superclass.constructor.call(this, config);
42538     this.addEvents({
42539         /**
42540          * @event check
42541          * Fires when the checkbox is checked or unchecked.
42542              * @param {Roo.form.Checkbox} this This checkbox
42543              * @param {Boolean} checked The new checked value
42544              */
42545         check : true
42546     });
42547 };
42548
42549 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42550     /**
42551      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42552      */
42553     focusClass : undefined,
42554     /**
42555      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42556      */
42557     fieldClass: "x-form-field",
42558     /**
42559      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42560      */
42561     checked: false,
42562     /**
42563      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42564      * {tag: "input", type: "checkbox", autocomplete: "off"})
42565      */
42566     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42567     /**
42568      * @cfg {String} boxLabel The text that appears beside the checkbox
42569      */
42570     boxLabel : "",
42571     /**
42572      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42573      */  
42574     inputValue : '1',
42575     /**
42576      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42577      */
42578      valueOff: '0', // value when not checked..
42579
42580     actionMode : 'viewEl', 
42581     //
42582     // private
42583     itemCls : 'x-menu-check-item x-form-item',
42584     groupClass : 'x-menu-group-item',
42585     inputType : 'hidden',
42586     
42587     
42588     inSetChecked: false, // check that we are not calling self...
42589     
42590     inputElement: false, // real input element?
42591     basedOn: false, // ????
42592     
42593     isFormField: true, // not sure where this is needed!!!!
42594
42595     onResize : function(){
42596         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42597         if(!this.boxLabel){
42598             this.el.alignTo(this.wrap, 'c-c');
42599         }
42600     },
42601
42602     initEvents : function(){
42603         Roo.form.Checkbox.superclass.initEvents.call(this);
42604         this.el.on("click", this.onClick,  this);
42605         this.el.on("change", this.onClick,  this);
42606     },
42607
42608
42609     getResizeEl : function(){
42610         return this.wrap;
42611     },
42612
42613     getPositionEl : function(){
42614         return this.wrap;
42615     },
42616
42617     // private
42618     onRender : function(ct, position){
42619         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42620         /*
42621         if(this.inputValue !== undefined){
42622             this.el.dom.value = this.inputValue;
42623         }
42624         */
42625         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42626         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42627         var viewEl = this.wrap.createChild({ 
42628             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42629         this.viewEl = viewEl;   
42630         this.wrap.on('click', this.onClick,  this); 
42631         
42632         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42633         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42634         
42635         
42636         
42637         if(this.boxLabel){
42638             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42639         //    viewEl.on('click', this.onClick,  this); 
42640         }
42641         //if(this.checked){
42642             this.setChecked(this.checked);
42643         //}else{
42644             //this.checked = this.el.dom;
42645         //}
42646
42647     },
42648
42649     // private
42650     initValue : Roo.emptyFn,
42651
42652     /**
42653      * Returns the checked state of the checkbox.
42654      * @return {Boolean} True if checked, else false
42655      */
42656     getValue : function(){
42657         if(this.el){
42658             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42659         }
42660         return this.valueOff;
42661         
42662     },
42663
42664         // private
42665     onClick : function(){ 
42666         if (this.disabled) {
42667             return;
42668         }
42669         this.setChecked(!this.checked);
42670
42671         //if(this.el.dom.checked != this.checked){
42672         //    this.setValue(this.el.dom.checked);
42673        // }
42674     },
42675
42676     /**
42677      * Sets the checked state of the checkbox.
42678      * On is always based on a string comparison between inputValue and the param.
42679      * @param {Boolean/String} value - the value to set 
42680      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42681      */
42682     setValue : function(v,suppressEvent){
42683         
42684         
42685         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42686         //if(this.el && this.el.dom){
42687         //    this.el.dom.checked = this.checked;
42688         //    this.el.dom.defaultChecked = this.checked;
42689         //}
42690         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42691         //this.fireEvent("check", this, this.checked);
42692     },
42693     // private..
42694     setChecked : function(state,suppressEvent)
42695     {
42696         if (this.inSetChecked) {
42697             this.checked = state;
42698             return;
42699         }
42700         
42701     
42702         if(this.wrap){
42703             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42704         }
42705         this.checked = state;
42706         if(suppressEvent !== true){
42707             this.fireEvent('check', this, state);
42708         }
42709         this.inSetChecked = true;
42710         this.el.dom.value = state ? this.inputValue : this.valueOff;
42711         this.inSetChecked = false;
42712         
42713     },
42714     // handle setting of hidden value by some other method!!?!?
42715     setFromHidden: function()
42716     {
42717         if(!this.el){
42718             return;
42719         }
42720         //console.log("SET FROM HIDDEN");
42721         //alert('setFrom hidden');
42722         this.setValue(this.el.dom.value);
42723     },
42724     
42725     onDestroy : function()
42726     {
42727         if(this.viewEl){
42728             Roo.get(this.viewEl).remove();
42729         }
42730          
42731         Roo.form.Checkbox.superclass.onDestroy.call(this);
42732     },
42733     
42734     setBoxLabel : function(str)
42735     {
42736         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42737     }
42738
42739 });/*
42740  * Based on:
42741  * Ext JS Library 1.1.1
42742  * Copyright(c) 2006-2007, Ext JS, LLC.
42743  *
42744  * Originally Released Under LGPL - original licence link has changed is not relivant.
42745  *
42746  * Fork - LGPL
42747  * <script type="text/javascript">
42748  */
42749  
42750 /**
42751  * @class Roo.form.Radio
42752  * @extends Roo.form.Checkbox
42753  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42754  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42755  * @constructor
42756  * Creates a new Radio
42757  * @param {Object} config Configuration options
42758  */
42759 Roo.form.Radio = function(){
42760     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42761 };
42762 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42763     inputType: 'radio',
42764
42765     /**
42766      * If this radio is part of a group, it will return the selected value
42767      * @return {String}
42768      */
42769     getGroupValue : function(){
42770         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42771     },
42772     
42773     
42774     onRender : function(ct, position){
42775         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42776         
42777         if(this.inputValue !== undefined){
42778             this.el.dom.value = this.inputValue;
42779         }
42780          
42781         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42782         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42783         //var viewEl = this.wrap.createChild({ 
42784         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42785         //this.viewEl = viewEl;   
42786         //this.wrap.on('click', this.onClick,  this); 
42787         
42788         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42789         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42790         
42791         
42792         
42793         if(this.boxLabel){
42794             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42795         //    viewEl.on('click', this.onClick,  this); 
42796         }
42797          if(this.checked){
42798             this.el.dom.checked =   'checked' ;
42799         }
42800          
42801     } 
42802     
42803     
42804 });//<script type="text/javascript">
42805
42806 /*
42807  * Based  Ext JS Library 1.1.1
42808  * Copyright(c) 2006-2007, Ext JS, LLC.
42809  * LGPL
42810  *
42811  */
42812  
42813 /**
42814  * @class Roo.HtmlEditorCore
42815  * @extends Roo.Component
42816  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42817  *
42818  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42819  */
42820
42821 Roo.HtmlEditorCore = function(config){
42822     
42823     
42824     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42825     
42826     
42827     this.addEvents({
42828         /**
42829          * @event initialize
42830          * Fires when the editor is fully initialized (including the iframe)
42831          * @param {Roo.HtmlEditorCore} this
42832          */
42833         initialize: true,
42834         /**
42835          * @event activate
42836          * Fires when the editor is first receives the focus. Any insertion must wait
42837          * until after this event.
42838          * @param {Roo.HtmlEditorCore} this
42839          */
42840         activate: true,
42841          /**
42842          * @event beforesync
42843          * Fires before the textarea is updated with content from the editor iframe. Return false
42844          * to cancel the sync.
42845          * @param {Roo.HtmlEditorCore} this
42846          * @param {String} html
42847          */
42848         beforesync: true,
42849          /**
42850          * @event beforepush
42851          * Fires before the iframe editor is updated with content from the textarea. Return false
42852          * to cancel the push.
42853          * @param {Roo.HtmlEditorCore} this
42854          * @param {String} html
42855          */
42856         beforepush: true,
42857          /**
42858          * @event sync
42859          * Fires when the textarea is updated with content from the editor iframe.
42860          * @param {Roo.HtmlEditorCore} this
42861          * @param {String} html
42862          */
42863         sync: true,
42864          /**
42865          * @event push
42866          * Fires when the iframe editor is updated with content from the textarea.
42867          * @param {Roo.HtmlEditorCore} this
42868          * @param {String} html
42869          */
42870         push: true,
42871         
42872         /**
42873          * @event editorevent
42874          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42875          * @param {Roo.HtmlEditorCore} this
42876          */
42877         editorevent: true
42878         
42879     });
42880     
42881     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42882     
42883     // defaults : white / black...
42884     this.applyBlacklists();
42885     
42886     
42887     
42888 };
42889
42890
42891 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42892
42893
42894      /**
42895      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42896      */
42897     
42898     owner : false,
42899     
42900      /**
42901      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42902      *                        Roo.resizable.
42903      */
42904     resizable : false,
42905      /**
42906      * @cfg {Number} height (in pixels)
42907      */   
42908     height: 300,
42909    /**
42910      * @cfg {Number} width (in pixels)
42911      */   
42912     width: 500,
42913     
42914     /**
42915      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42916      * 
42917      */
42918     stylesheets: false,
42919     
42920     // id of frame..
42921     frameId: false,
42922     
42923     // private properties
42924     validationEvent : false,
42925     deferHeight: true,
42926     initialized : false,
42927     activated : false,
42928     sourceEditMode : false,
42929     onFocus : Roo.emptyFn,
42930     iframePad:3,
42931     hideMode:'offsets',
42932     
42933     clearUp: true,
42934     
42935     // blacklist + whitelisted elements..
42936     black: false,
42937     white: false,
42938      
42939     bodyCls : '',
42940
42941     /**
42942      * Protected method that will not generally be called directly. It
42943      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42944      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42945      */
42946     getDocMarkup : function(){
42947         // body styles..
42948         var st = '';
42949         
42950         // inherit styels from page...?? 
42951         if (this.stylesheets === false) {
42952             
42953             Roo.get(document.head).select('style').each(function(node) {
42954                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42955             });
42956             
42957             Roo.get(document.head).select('link').each(function(node) { 
42958                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42959             });
42960             
42961         } else if (!this.stylesheets.length) {
42962                 // simple..
42963                 st = '<style type="text/css">' +
42964                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42965                    '</style>';
42966         } else { 
42967             st = '<style type="text/css">' +
42968                     this.stylesheets +
42969                 '</style>';
42970         }
42971         
42972         st +=  '<style type="text/css">' +
42973             'IMG { cursor: pointer } ' +
42974         '</style>';
42975
42976         var cls = 'roo-htmleditor-body';
42977         
42978         if(this.bodyCls.length){
42979             cls += ' ' + this.bodyCls;
42980         }
42981         
42982         return '<html><head>' + st  +
42983             //<style type="text/css">' +
42984             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42985             //'</style>' +
42986             ' </head><body class="' +  cls + '"></body></html>';
42987     },
42988
42989     // private
42990     onRender : function(ct, position)
42991     {
42992         var _t = this;
42993         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42994         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42995         
42996         
42997         this.el.dom.style.border = '0 none';
42998         this.el.dom.setAttribute('tabIndex', -1);
42999         this.el.addClass('x-hidden hide');
43000         
43001         
43002         
43003         if(Roo.isIE){ // fix IE 1px bogus margin
43004             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43005         }
43006        
43007         
43008         this.frameId = Roo.id();
43009         
43010          
43011         
43012         var iframe = this.owner.wrap.createChild({
43013             tag: 'iframe',
43014             cls: 'form-control', // bootstrap..
43015             id: this.frameId,
43016             name: this.frameId,
43017             frameBorder : 'no',
43018             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43019         }, this.el
43020         );
43021         
43022         
43023         this.iframe = iframe.dom;
43024
43025          this.assignDocWin();
43026         
43027         this.doc.designMode = 'on';
43028        
43029         this.doc.open();
43030         this.doc.write(this.getDocMarkup());
43031         this.doc.close();
43032
43033         
43034         var task = { // must defer to wait for browser to be ready
43035             run : function(){
43036                 //console.log("run task?" + this.doc.readyState);
43037                 this.assignDocWin();
43038                 if(this.doc.body || this.doc.readyState == 'complete'){
43039                     try {
43040                         this.doc.designMode="on";
43041                     } catch (e) {
43042                         return;
43043                     }
43044                     Roo.TaskMgr.stop(task);
43045                     this.initEditor.defer(10, this);
43046                 }
43047             },
43048             interval : 10,
43049             duration: 10000,
43050             scope: this
43051         };
43052         Roo.TaskMgr.start(task);
43053
43054     },
43055
43056     // private
43057     onResize : function(w, h)
43058     {
43059          Roo.log('resize: ' +w + ',' + h );
43060         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43061         if(!this.iframe){
43062             return;
43063         }
43064         if(typeof w == 'number'){
43065             
43066             this.iframe.style.width = w + 'px';
43067         }
43068         if(typeof h == 'number'){
43069             
43070             this.iframe.style.height = h + 'px';
43071             if(this.doc){
43072                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43073             }
43074         }
43075         
43076     },
43077
43078     /**
43079      * Toggles the editor between standard and source edit mode.
43080      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43081      */
43082     toggleSourceEdit : function(sourceEditMode){
43083         
43084         this.sourceEditMode = sourceEditMode === true;
43085         
43086         if(this.sourceEditMode){
43087  
43088             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43089             
43090         }else{
43091             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43092             //this.iframe.className = '';
43093             this.deferFocus();
43094         }
43095         //this.setSize(this.owner.wrap.getSize());
43096         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43097     },
43098
43099     
43100   
43101
43102     /**
43103      * Protected method that will not generally be called directly. If you need/want
43104      * custom HTML cleanup, this is the method you should override.
43105      * @param {String} html The HTML to be cleaned
43106      * return {String} The cleaned HTML
43107      */
43108     cleanHtml : function(html){
43109         html = String(html);
43110         if(html.length > 5){
43111             if(Roo.isSafari){ // strip safari nonsense
43112                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43113             }
43114         }
43115         if(html == '&nbsp;'){
43116             html = '';
43117         }
43118         return html;
43119     },
43120
43121     /**
43122      * HTML Editor -> Textarea
43123      * Protected method that will not generally be called directly. Syncs the contents
43124      * of the editor iframe with the textarea.
43125      */
43126     syncValue : function(){
43127         if(this.initialized){
43128             var bd = (this.doc.body || this.doc.documentElement);
43129             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43130             var html = bd.innerHTML;
43131             if(Roo.isSafari){
43132                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43133                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43134                 if(m && m[1]){
43135                     html = '<div style="'+m[0]+'">' + html + '</div>';
43136                 }
43137             }
43138             html = this.cleanHtml(html);
43139             // fix up the special chars.. normaly like back quotes in word...
43140             // however we do not want to do this with chinese..
43141             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
43142                 var cc = b.charCodeAt();
43143                 if (
43144                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43145                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43146                     (cc >= 0xf900 && cc < 0xfb00 )
43147                 ) {
43148                         return b;
43149                 }
43150                 return "&#"+cc+";" 
43151             });
43152             if(this.owner.fireEvent('beforesync', this, html) !== false){
43153                 this.el.dom.value = html;
43154                 this.owner.fireEvent('sync', this, html);
43155             }
43156         }
43157     },
43158
43159     /**
43160      * Protected method that will not generally be called directly. Pushes the value of the textarea
43161      * into the iframe editor.
43162      */
43163     pushValue : function(){
43164         if(this.initialized){
43165             var v = this.el.dom.value.trim();
43166             
43167 //            if(v.length < 1){
43168 //                v = '&#160;';
43169 //            }
43170             
43171             if(this.owner.fireEvent('beforepush', this, v) !== false){
43172                 var d = (this.doc.body || this.doc.documentElement);
43173                 d.innerHTML = v;
43174                 this.cleanUpPaste();
43175                 this.el.dom.value = d.innerHTML;
43176                 this.owner.fireEvent('push', this, v);
43177             }
43178         }
43179     },
43180
43181     // private
43182     deferFocus : function(){
43183         this.focus.defer(10, this);
43184     },
43185
43186     // doc'ed in Field
43187     focus : function(){
43188         if(this.win && !this.sourceEditMode){
43189             this.win.focus();
43190         }else{
43191             this.el.focus();
43192         }
43193     },
43194     
43195     assignDocWin: function()
43196     {
43197         var iframe = this.iframe;
43198         
43199          if(Roo.isIE){
43200             this.doc = iframe.contentWindow.document;
43201             this.win = iframe.contentWindow;
43202         } else {
43203 //            if (!Roo.get(this.frameId)) {
43204 //                return;
43205 //            }
43206 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43207 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43208             
43209             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43210                 return;
43211             }
43212             
43213             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43214             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43215         }
43216     },
43217     
43218     // private
43219     initEditor : function(){
43220         //console.log("INIT EDITOR");
43221         this.assignDocWin();
43222         
43223         
43224         
43225         this.doc.designMode="on";
43226         this.doc.open();
43227         this.doc.write(this.getDocMarkup());
43228         this.doc.close();
43229         
43230         var dbody = (this.doc.body || this.doc.documentElement);
43231         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43232         // this copies styles from the containing element into thsi one..
43233         // not sure why we need all of this..
43234         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43235         
43236         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43237         //ss['background-attachment'] = 'fixed'; // w3c
43238         dbody.bgProperties = 'fixed'; // ie
43239         //Roo.DomHelper.applyStyles(dbody, ss);
43240         Roo.EventManager.on(this.doc, {
43241             //'mousedown': this.onEditorEvent,
43242             'mouseup': this.onEditorEvent,
43243             'dblclick': this.onEditorEvent,
43244             'click': this.onEditorEvent,
43245             'keyup': this.onEditorEvent,
43246             buffer:100,
43247             scope: this
43248         });
43249         if(Roo.isGecko){
43250             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43251         }
43252         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43253             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43254         }
43255         this.initialized = true;
43256
43257         this.owner.fireEvent('initialize', this);
43258         this.pushValue();
43259     },
43260
43261     // private
43262     onDestroy : function(){
43263         
43264         
43265         
43266         if(this.rendered){
43267             
43268             //for (var i =0; i < this.toolbars.length;i++) {
43269             //    // fixme - ask toolbars for heights?
43270             //    this.toolbars[i].onDestroy();
43271            // }
43272             
43273             //this.wrap.dom.innerHTML = '';
43274             //this.wrap.remove();
43275         }
43276     },
43277
43278     // private
43279     onFirstFocus : function(){
43280         
43281         this.assignDocWin();
43282         
43283         
43284         this.activated = true;
43285          
43286     
43287         if(Roo.isGecko){ // prevent silly gecko errors
43288             this.win.focus();
43289             var s = this.win.getSelection();
43290             if(!s.focusNode || s.focusNode.nodeType != 3){
43291                 var r = s.getRangeAt(0);
43292                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43293                 r.collapse(true);
43294                 this.deferFocus();
43295             }
43296             try{
43297                 this.execCmd('useCSS', true);
43298                 this.execCmd('styleWithCSS', false);
43299             }catch(e){}
43300         }
43301         this.owner.fireEvent('activate', this);
43302     },
43303
43304     // private
43305     adjustFont: function(btn){
43306         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43307         //if(Roo.isSafari){ // safari
43308         //    adjust *= 2;
43309        // }
43310         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43311         if(Roo.isSafari){ // safari
43312             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43313             v =  (v < 10) ? 10 : v;
43314             v =  (v > 48) ? 48 : v;
43315             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43316             
43317         }
43318         
43319         
43320         v = Math.max(1, v+adjust);
43321         
43322         this.execCmd('FontSize', v  );
43323     },
43324
43325     onEditorEvent : function(e)
43326     {
43327         this.owner.fireEvent('editorevent', this, e);
43328       //  this.updateToolbar();
43329         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43330     },
43331
43332     insertTag : function(tg)
43333     {
43334         // could be a bit smarter... -> wrap the current selected tRoo..
43335         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43336             
43337             range = this.createRange(this.getSelection());
43338             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43339             wrappingNode.appendChild(range.extractContents());
43340             range.insertNode(wrappingNode);
43341
43342             return;
43343             
43344             
43345             
43346         }
43347         this.execCmd("formatblock",   tg);
43348         
43349     },
43350     
43351     insertText : function(txt)
43352     {
43353         
43354         
43355         var range = this.createRange();
43356         range.deleteContents();
43357                //alert(Sender.getAttribute('label'));
43358                
43359         range.insertNode(this.doc.createTextNode(txt));
43360     } ,
43361     
43362      
43363
43364     /**
43365      * Executes a Midas editor command on the editor document and performs necessary focus and
43366      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43367      * @param {String} cmd The Midas command
43368      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43369      */
43370     relayCmd : function(cmd, value){
43371         this.win.focus();
43372         this.execCmd(cmd, value);
43373         this.owner.fireEvent('editorevent', this);
43374         //this.updateToolbar();
43375         this.owner.deferFocus();
43376     },
43377
43378     /**
43379      * Executes a Midas editor command directly on the editor document.
43380      * For visual commands, you should use {@link #relayCmd} instead.
43381      * <b>This should only be called after the editor is initialized.</b>
43382      * @param {String} cmd The Midas command
43383      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43384      */
43385     execCmd : function(cmd, value){
43386         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43387         this.syncValue();
43388     },
43389  
43390  
43391    
43392     /**
43393      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43394      * to insert tRoo.
43395      * @param {String} text | dom node.. 
43396      */
43397     insertAtCursor : function(text)
43398     {
43399         
43400         if(!this.activated){
43401             return;
43402         }
43403         /*
43404         if(Roo.isIE){
43405             this.win.focus();
43406             var r = this.doc.selection.createRange();
43407             if(r){
43408                 r.collapse(true);
43409                 r.pasteHTML(text);
43410                 this.syncValue();
43411                 this.deferFocus();
43412             
43413             }
43414             return;
43415         }
43416         */
43417         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43418             this.win.focus();
43419             
43420             
43421             // from jquery ui (MIT licenced)
43422             var range, node;
43423             var win = this.win;
43424             
43425             if (win.getSelection && win.getSelection().getRangeAt) {
43426                 range = win.getSelection().getRangeAt(0);
43427                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43428                 range.insertNode(node);
43429             } else if (win.document.selection && win.document.selection.createRange) {
43430                 // no firefox support
43431                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43432                 win.document.selection.createRange().pasteHTML(txt);
43433             } else {
43434                 // no firefox support
43435                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43436                 this.execCmd('InsertHTML', txt);
43437             } 
43438             
43439             this.syncValue();
43440             
43441             this.deferFocus();
43442         }
43443     },
43444  // private
43445     mozKeyPress : function(e){
43446         if(e.ctrlKey){
43447             var c = e.getCharCode(), cmd;
43448           
43449             if(c > 0){
43450                 c = String.fromCharCode(c).toLowerCase();
43451                 switch(c){
43452                     case 'b':
43453                         cmd = 'bold';
43454                         break;
43455                     case 'i':
43456                         cmd = 'italic';
43457                         break;
43458                     
43459                     case 'u':
43460                         cmd = 'underline';
43461                         break;
43462                     
43463                     case 'v':
43464                         this.cleanUpPaste.defer(100, this);
43465                         return;
43466                         
43467                 }
43468                 if(cmd){
43469                     this.win.focus();
43470                     this.execCmd(cmd);
43471                     this.deferFocus();
43472                     e.preventDefault();
43473                 }
43474                 
43475             }
43476         }
43477     },
43478
43479     // private
43480     fixKeys : function(){ // load time branching for fastest keydown performance
43481         if(Roo.isIE){
43482             return function(e){
43483                 var k = e.getKey(), r;
43484                 if(k == e.TAB){
43485                     e.stopEvent();
43486                     r = this.doc.selection.createRange();
43487                     if(r){
43488                         r.collapse(true);
43489                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43490                         this.deferFocus();
43491                     }
43492                     return;
43493                 }
43494                 
43495                 if(k == e.ENTER){
43496                     r = this.doc.selection.createRange();
43497                     if(r){
43498                         var target = r.parentElement();
43499                         if(!target || target.tagName.toLowerCase() != 'li'){
43500                             e.stopEvent();
43501                             r.pasteHTML('<br />');
43502                             r.collapse(false);
43503                             r.select();
43504                         }
43505                     }
43506                 }
43507                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43508                     this.cleanUpPaste.defer(100, this);
43509                     return;
43510                 }
43511                 
43512                 
43513             };
43514         }else if(Roo.isOpera){
43515             return function(e){
43516                 var k = e.getKey();
43517                 if(k == e.TAB){
43518                     e.stopEvent();
43519                     this.win.focus();
43520                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43521                     this.deferFocus();
43522                 }
43523                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43524                     this.cleanUpPaste.defer(100, this);
43525                     return;
43526                 }
43527                 
43528             };
43529         }else if(Roo.isSafari){
43530             return function(e){
43531                 var k = e.getKey();
43532                 
43533                 if(k == e.TAB){
43534                     e.stopEvent();
43535                     this.execCmd('InsertText','\t');
43536                     this.deferFocus();
43537                     return;
43538                 }
43539                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43540                     this.cleanUpPaste.defer(100, this);
43541                     return;
43542                 }
43543                 
43544              };
43545         }
43546     }(),
43547     
43548     getAllAncestors: function()
43549     {
43550         var p = this.getSelectedNode();
43551         var a = [];
43552         if (!p) {
43553             a.push(p); // push blank onto stack..
43554             p = this.getParentElement();
43555         }
43556         
43557         
43558         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43559             a.push(p);
43560             p = p.parentNode;
43561         }
43562         a.push(this.doc.body);
43563         return a;
43564     },
43565     lastSel : false,
43566     lastSelNode : false,
43567     
43568     
43569     getSelection : function() 
43570     {
43571         this.assignDocWin();
43572         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43573     },
43574     
43575     getSelectedNode: function() 
43576     {
43577         // this may only work on Gecko!!!
43578         
43579         // should we cache this!!!!
43580         
43581         
43582         
43583          
43584         var range = this.createRange(this.getSelection()).cloneRange();
43585         
43586         if (Roo.isIE) {
43587             var parent = range.parentElement();
43588             while (true) {
43589                 var testRange = range.duplicate();
43590                 testRange.moveToElementText(parent);
43591                 if (testRange.inRange(range)) {
43592                     break;
43593                 }
43594                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43595                     break;
43596                 }
43597                 parent = parent.parentElement;
43598             }
43599             return parent;
43600         }
43601         
43602         // is ancestor a text element.
43603         var ac =  range.commonAncestorContainer;
43604         if (ac.nodeType == 3) {
43605             ac = ac.parentNode;
43606         }
43607         
43608         var ar = ac.childNodes;
43609          
43610         var nodes = [];
43611         var other_nodes = [];
43612         var has_other_nodes = false;
43613         for (var i=0;i<ar.length;i++) {
43614             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43615                 continue;
43616             }
43617             // fullly contained node.
43618             
43619             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43620                 nodes.push(ar[i]);
43621                 continue;
43622             }
43623             
43624             // probably selected..
43625             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43626                 other_nodes.push(ar[i]);
43627                 continue;
43628             }
43629             // outer..
43630             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43631                 continue;
43632             }
43633             
43634             
43635             has_other_nodes = true;
43636         }
43637         if (!nodes.length && other_nodes.length) {
43638             nodes= other_nodes;
43639         }
43640         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43641             return false;
43642         }
43643         
43644         return nodes[0];
43645     },
43646     createRange: function(sel)
43647     {
43648         // this has strange effects when using with 
43649         // top toolbar - not sure if it's a great idea.
43650         //this.editor.contentWindow.focus();
43651         if (typeof sel != "undefined") {
43652             try {
43653                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43654             } catch(e) {
43655                 return this.doc.createRange();
43656             }
43657         } else {
43658             return this.doc.createRange();
43659         }
43660     },
43661     getParentElement: function()
43662     {
43663         
43664         this.assignDocWin();
43665         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43666         
43667         var range = this.createRange(sel);
43668          
43669         try {
43670             var p = range.commonAncestorContainer;
43671             while (p.nodeType == 3) { // text node
43672                 p = p.parentNode;
43673             }
43674             return p;
43675         } catch (e) {
43676             return null;
43677         }
43678     
43679     },
43680     /***
43681      *
43682      * Range intersection.. the hard stuff...
43683      *  '-1' = before
43684      *  '0' = hits..
43685      *  '1' = after.
43686      *         [ -- selected range --- ]
43687      *   [fail]                        [fail]
43688      *
43689      *    basically..
43690      *      if end is before start or  hits it. fail.
43691      *      if start is after end or hits it fail.
43692      *
43693      *   if either hits (but other is outside. - then it's not 
43694      *   
43695      *    
43696      **/
43697     
43698     
43699     // @see http://www.thismuchiknow.co.uk/?p=64.
43700     rangeIntersectsNode : function(range, node)
43701     {
43702         var nodeRange = node.ownerDocument.createRange();
43703         try {
43704             nodeRange.selectNode(node);
43705         } catch (e) {
43706             nodeRange.selectNodeContents(node);
43707         }
43708     
43709         var rangeStartRange = range.cloneRange();
43710         rangeStartRange.collapse(true);
43711     
43712         var rangeEndRange = range.cloneRange();
43713         rangeEndRange.collapse(false);
43714     
43715         var nodeStartRange = nodeRange.cloneRange();
43716         nodeStartRange.collapse(true);
43717     
43718         var nodeEndRange = nodeRange.cloneRange();
43719         nodeEndRange.collapse(false);
43720     
43721         return rangeStartRange.compareBoundaryPoints(
43722                  Range.START_TO_START, nodeEndRange) == -1 &&
43723                rangeEndRange.compareBoundaryPoints(
43724                  Range.START_TO_START, nodeStartRange) == 1;
43725         
43726          
43727     },
43728     rangeCompareNode : function(range, node)
43729     {
43730         var nodeRange = node.ownerDocument.createRange();
43731         try {
43732             nodeRange.selectNode(node);
43733         } catch (e) {
43734             nodeRange.selectNodeContents(node);
43735         }
43736         
43737         
43738         range.collapse(true);
43739     
43740         nodeRange.collapse(true);
43741      
43742         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43743         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43744          
43745         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43746         
43747         var nodeIsBefore   =  ss == 1;
43748         var nodeIsAfter    = ee == -1;
43749         
43750         if (nodeIsBefore && nodeIsAfter) {
43751             return 0; // outer
43752         }
43753         if (!nodeIsBefore && nodeIsAfter) {
43754             return 1; //right trailed.
43755         }
43756         
43757         if (nodeIsBefore && !nodeIsAfter) {
43758             return 2;  // left trailed.
43759         }
43760         // fully contined.
43761         return 3;
43762     },
43763
43764     // private? - in a new class?
43765     cleanUpPaste :  function()
43766     {
43767         // cleans up the whole document..
43768         Roo.log('cleanuppaste');
43769         
43770         this.cleanUpChildren(this.doc.body);
43771         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43772         if (clean != this.doc.body.innerHTML) {
43773             this.doc.body.innerHTML = clean;
43774         }
43775         
43776     },
43777     
43778     cleanWordChars : function(input) {// change the chars to hex code
43779         var he = Roo.HtmlEditorCore;
43780         
43781         var output = input;
43782         Roo.each(he.swapCodes, function(sw) { 
43783             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43784             
43785             output = output.replace(swapper, sw[1]);
43786         });
43787         
43788         return output;
43789     },
43790     
43791     
43792     cleanUpChildren : function (n)
43793     {
43794         if (!n.childNodes.length) {
43795             return;
43796         }
43797         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43798            this.cleanUpChild(n.childNodes[i]);
43799         }
43800     },
43801     
43802     
43803         
43804     
43805     cleanUpChild : function (node)
43806     {
43807         var ed = this;
43808         //console.log(node);
43809         if (node.nodeName == "#text") {
43810             // clean up silly Windows -- stuff?
43811             return; 
43812         }
43813         if (node.nodeName == "#comment") {
43814             node.parentNode.removeChild(node);
43815             // clean up silly Windows -- stuff?
43816             return; 
43817         }
43818         var lcname = node.tagName.toLowerCase();
43819         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43820         // whitelist of tags..
43821         
43822         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43823             // remove node.
43824             node.parentNode.removeChild(node);
43825             return;
43826             
43827         }
43828         
43829         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43830         
43831         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43832         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43833         
43834         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43835         //    remove_keep_children = true;
43836         //}
43837         
43838         if (remove_keep_children) {
43839             this.cleanUpChildren(node);
43840             // inserts everything just before this node...
43841             while (node.childNodes.length) {
43842                 var cn = node.childNodes[0];
43843                 node.removeChild(cn);
43844                 node.parentNode.insertBefore(cn, node);
43845             }
43846             node.parentNode.removeChild(node);
43847             return;
43848         }
43849         
43850         if (!node.attributes || !node.attributes.length) {
43851             this.cleanUpChildren(node);
43852             return;
43853         }
43854         
43855         function cleanAttr(n,v)
43856         {
43857             
43858             if (v.match(/^\./) || v.match(/^\//)) {
43859                 return;
43860             }
43861             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
43862                 return;
43863             }
43864             if (v.match(/^#/)) {
43865                 return;
43866             }
43867 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43868             node.removeAttribute(n);
43869             
43870         }
43871         
43872         var cwhite = this.cwhite;
43873         var cblack = this.cblack;
43874             
43875         function cleanStyle(n,v)
43876         {
43877             if (v.match(/expression/)) { //XSS?? should we even bother..
43878                 node.removeAttribute(n);
43879                 return;
43880             }
43881             
43882             var parts = v.split(/;/);
43883             var clean = [];
43884             
43885             Roo.each(parts, function(p) {
43886                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43887                 if (!p.length) {
43888                     return true;
43889                 }
43890                 var l = p.split(':').shift().replace(/\s+/g,'');
43891                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43892                 
43893                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43894 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43895                     //node.removeAttribute(n);
43896                     return true;
43897                 }
43898                 //Roo.log()
43899                 // only allow 'c whitelisted system attributes'
43900                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43901 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43902                     //node.removeAttribute(n);
43903                     return true;
43904                 }
43905                 
43906                 
43907                  
43908                 
43909                 clean.push(p);
43910                 return true;
43911             });
43912             if (clean.length) { 
43913                 node.setAttribute(n, clean.join(';'));
43914             } else {
43915                 node.removeAttribute(n);
43916             }
43917             
43918         }
43919         
43920         
43921         for (var i = node.attributes.length-1; i > -1 ; i--) {
43922             var a = node.attributes[i];
43923             //console.log(a);
43924             
43925             if (a.name.toLowerCase().substr(0,2)=='on')  {
43926                 node.removeAttribute(a.name);
43927                 continue;
43928             }
43929             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43930                 node.removeAttribute(a.name);
43931                 continue;
43932             }
43933             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43934                 cleanAttr(a.name,a.value); // fixme..
43935                 continue;
43936             }
43937             if (a.name == 'style') {
43938                 cleanStyle(a.name,a.value);
43939                 continue;
43940             }
43941             /// clean up MS crap..
43942             // tecnically this should be a list of valid class'es..
43943             
43944             
43945             if (a.name == 'class') {
43946                 if (a.value.match(/^Mso/)) {
43947                     node.className = '';
43948                 }
43949                 
43950                 if (a.value.match(/^body$/)) {
43951                     node.className = '';
43952                 }
43953                 continue;
43954             }
43955             
43956             // style cleanup!?
43957             // class cleanup?
43958             
43959         }
43960         
43961         
43962         this.cleanUpChildren(node);
43963         
43964         
43965     },
43966     
43967     /**
43968      * Clean up MS wordisms...
43969      */
43970     cleanWord : function(node)
43971     {
43972         
43973         
43974         if (!node) {
43975             this.cleanWord(this.doc.body);
43976             return;
43977         }
43978         if (node.nodeName == "#text") {
43979             // clean up silly Windows -- stuff?
43980             return; 
43981         }
43982         if (node.nodeName == "#comment") {
43983             node.parentNode.removeChild(node);
43984             // clean up silly Windows -- stuff?
43985             return; 
43986         }
43987         
43988         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43989             node.parentNode.removeChild(node);
43990             return;
43991         }
43992         
43993         // remove - but keep children..
43994         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43995             while (node.childNodes.length) {
43996                 var cn = node.childNodes[0];
43997                 node.removeChild(cn);
43998                 node.parentNode.insertBefore(cn, node);
43999             }
44000             node.parentNode.removeChild(node);
44001             this.iterateChildren(node, this.cleanWord);
44002             return;
44003         }
44004         // clean styles
44005         if (node.className.length) {
44006             
44007             var cn = node.className.split(/\W+/);
44008             var cna = [];
44009             Roo.each(cn, function(cls) {
44010                 if (cls.match(/Mso[a-zA-Z]+/)) {
44011                     return;
44012                 }
44013                 cna.push(cls);
44014             });
44015             node.className = cna.length ? cna.join(' ') : '';
44016             if (!cna.length) {
44017                 node.removeAttribute("class");
44018             }
44019         }
44020         
44021         if (node.hasAttribute("lang")) {
44022             node.removeAttribute("lang");
44023         }
44024         
44025         if (node.hasAttribute("style")) {
44026             
44027             var styles = node.getAttribute("style").split(";");
44028             var nstyle = [];
44029             Roo.each(styles, function(s) {
44030                 if (!s.match(/:/)) {
44031                     return;
44032                 }
44033                 var kv = s.split(":");
44034                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44035                     return;
44036                 }
44037                 // what ever is left... we allow.
44038                 nstyle.push(s);
44039             });
44040             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44041             if (!nstyle.length) {
44042                 node.removeAttribute('style');
44043             }
44044         }
44045         this.iterateChildren(node, this.cleanWord);
44046         
44047         
44048         
44049     },
44050     /**
44051      * iterateChildren of a Node, calling fn each time, using this as the scole..
44052      * @param {DomNode} node node to iterate children of.
44053      * @param {Function} fn method of this class to call on each item.
44054      */
44055     iterateChildren : function(node, fn)
44056     {
44057         if (!node.childNodes.length) {
44058                 return;
44059         }
44060         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44061            fn.call(this, node.childNodes[i])
44062         }
44063     },
44064     
44065     
44066     /**
44067      * cleanTableWidths.
44068      *
44069      * Quite often pasting from word etc.. results in tables with column and widths.
44070      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44071      *
44072      */
44073     cleanTableWidths : function(node)
44074     {
44075          
44076          
44077         if (!node) {
44078             this.cleanTableWidths(this.doc.body);
44079             return;
44080         }
44081         
44082         // ignore list...
44083         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44084             return; 
44085         }
44086         Roo.log(node.tagName);
44087         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44088             this.iterateChildren(node, this.cleanTableWidths);
44089             return;
44090         }
44091         if (node.hasAttribute('width')) {
44092             node.removeAttribute('width');
44093         }
44094         
44095          
44096         if (node.hasAttribute("style")) {
44097             // pretty basic...
44098             
44099             var styles = node.getAttribute("style").split(";");
44100             var nstyle = [];
44101             Roo.each(styles, function(s) {
44102                 if (!s.match(/:/)) {
44103                     return;
44104                 }
44105                 var kv = s.split(":");
44106                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44107                     return;
44108                 }
44109                 // what ever is left... we allow.
44110                 nstyle.push(s);
44111             });
44112             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44113             if (!nstyle.length) {
44114                 node.removeAttribute('style');
44115             }
44116         }
44117         
44118         this.iterateChildren(node, this.cleanTableWidths);
44119         
44120         
44121     },
44122     
44123     
44124     
44125     
44126     domToHTML : function(currentElement, depth, nopadtext) {
44127         
44128         depth = depth || 0;
44129         nopadtext = nopadtext || false;
44130     
44131         if (!currentElement) {
44132             return this.domToHTML(this.doc.body);
44133         }
44134         
44135         //Roo.log(currentElement);
44136         var j;
44137         var allText = false;
44138         var nodeName = currentElement.nodeName;
44139         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44140         
44141         if  (nodeName == '#text') {
44142             
44143             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44144         }
44145         
44146         
44147         var ret = '';
44148         if (nodeName != 'BODY') {
44149              
44150             var i = 0;
44151             // Prints the node tagName, such as <A>, <IMG>, etc
44152             if (tagName) {
44153                 var attr = [];
44154                 for(i = 0; i < currentElement.attributes.length;i++) {
44155                     // quoting?
44156                     var aname = currentElement.attributes.item(i).name;
44157                     if (!currentElement.attributes.item(i).value.length) {
44158                         continue;
44159                     }
44160                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44161                 }
44162                 
44163                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44164             } 
44165             else {
44166                 
44167                 // eack
44168             }
44169         } else {
44170             tagName = false;
44171         }
44172         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44173             return ret;
44174         }
44175         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44176             nopadtext = true;
44177         }
44178         
44179         
44180         // Traverse the tree
44181         i = 0;
44182         var currentElementChild = currentElement.childNodes.item(i);
44183         var allText = true;
44184         var innerHTML  = '';
44185         lastnode = '';
44186         while (currentElementChild) {
44187             // Formatting code (indent the tree so it looks nice on the screen)
44188             var nopad = nopadtext;
44189             if (lastnode == 'SPAN') {
44190                 nopad  = true;
44191             }
44192             // text
44193             if  (currentElementChild.nodeName == '#text') {
44194                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44195                 toadd = nopadtext ? toadd : toadd.trim();
44196                 if (!nopad && toadd.length > 80) {
44197                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44198                 }
44199                 innerHTML  += toadd;
44200                 
44201                 i++;
44202                 currentElementChild = currentElement.childNodes.item(i);
44203                 lastNode = '';
44204                 continue;
44205             }
44206             allText = false;
44207             
44208             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44209                 
44210             // Recursively traverse the tree structure of the child node
44211             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44212             lastnode = currentElementChild.nodeName;
44213             i++;
44214             currentElementChild=currentElement.childNodes.item(i);
44215         }
44216         
44217         ret += innerHTML;
44218         
44219         if (!allText) {
44220                 // The remaining code is mostly for formatting the tree
44221             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44222         }
44223         
44224         
44225         if (tagName) {
44226             ret+= "</"+tagName+">";
44227         }
44228         return ret;
44229         
44230     },
44231         
44232     applyBlacklists : function()
44233     {
44234         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44235         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44236         
44237         this.white = [];
44238         this.black = [];
44239         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44240             if (b.indexOf(tag) > -1) {
44241                 return;
44242             }
44243             this.white.push(tag);
44244             
44245         }, this);
44246         
44247         Roo.each(w, function(tag) {
44248             if (b.indexOf(tag) > -1) {
44249                 return;
44250             }
44251             if (this.white.indexOf(tag) > -1) {
44252                 return;
44253             }
44254             this.white.push(tag);
44255             
44256         }, this);
44257         
44258         
44259         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44260             if (w.indexOf(tag) > -1) {
44261                 return;
44262             }
44263             this.black.push(tag);
44264             
44265         }, this);
44266         
44267         Roo.each(b, function(tag) {
44268             if (w.indexOf(tag) > -1) {
44269                 return;
44270             }
44271             if (this.black.indexOf(tag) > -1) {
44272                 return;
44273             }
44274             this.black.push(tag);
44275             
44276         }, this);
44277         
44278         
44279         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44280         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44281         
44282         this.cwhite = [];
44283         this.cblack = [];
44284         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44285             if (b.indexOf(tag) > -1) {
44286                 return;
44287             }
44288             this.cwhite.push(tag);
44289             
44290         }, this);
44291         
44292         Roo.each(w, function(tag) {
44293             if (b.indexOf(tag) > -1) {
44294                 return;
44295             }
44296             if (this.cwhite.indexOf(tag) > -1) {
44297                 return;
44298             }
44299             this.cwhite.push(tag);
44300             
44301         }, this);
44302         
44303         
44304         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44305             if (w.indexOf(tag) > -1) {
44306                 return;
44307             }
44308             this.cblack.push(tag);
44309             
44310         }, this);
44311         
44312         Roo.each(b, function(tag) {
44313             if (w.indexOf(tag) > -1) {
44314                 return;
44315             }
44316             if (this.cblack.indexOf(tag) > -1) {
44317                 return;
44318             }
44319             this.cblack.push(tag);
44320             
44321         }, this);
44322     },
44323     
44324     setStylesheets : function(stylesheets)
44325     {
44326         if(typeof(stylesheets) == 'string'){
44327             Roo.get(this.iframe.contentDocument.head).createChild({
44328                 tag : 'link',
44329                 rel : 'stylesheet',
44330                 type : 'text/css',
44331                 href : stylesheets
44332             });
44333             
44334             return;
44335         }
44336         var _this = this;
44337      
44338         Roo.each(stylesheets, function(s) {
44339             if(!s.length){
44340                 return;
44341             }
44342             
44343             Roo.get(_this.iframe.contentDocument.head).createChild({
44344                 tag : 'link',
44345                 rel : 'stylesheet',
44346                 type : 'text/css',
44347                 href : s
44348             });
44349         });
44350
44351         
44352     },
44353     
44354     removeStylesheets : function()
44355     {
44356         var _this = this;
44357         
44358         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44359             s.remove();
44360         });
44361     },
44362     
44363     setStyle : function(style)
44364     {
44365         Roo.get(this.iframe.contentDocument.head).createChild({
44366             tag : 'style',
44367             type : 'text/css',
44368             html : style
44369         });
44370
44371         return;
44372     }
44373     
44374     // hide stuff that is not compatible
44375     /**
44376      * @event blur
44377      * @hide
44378      */
44379     /**
44380      * @event change
44381      * @hide
44382      */
44383     /**
44384      * @event focus
44385      * @hide
44386      */
44387     /**
44388      * @event specialkey
44389      * @hide
44390      */
44391     /**
44392      * @cfg {String} fieldClass @hide
44393      */
44394     /**
44395      * @cfg {String} focusClass @hide
44396      */
44397     /**
44398      * @cfg {String} autoCreate @hide
44399      */
44400     /**
44401      * @cfg {String} inputType @hide
44402      */
44403     /**
44404      * @cfg {String} invalidClass @hide
44405      */
44406     /**
44407      * @cfg {String} invalidText @hide
44408      */
44409     /**
44410      * @cfg {String} msgFx @hide
44411      */
44412     /**
44413      * @cfg {String} validateOnBlur @hide
44414      */
44415 });
44416
44417 Roo.HtmlEditorCore.white = [
44418         'area', 'br', 'img', 'input', 'hr', 'wbr',
44419         
44420        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44421        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44422        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44423        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44424        'table',   'ul',         'xmp', 
44425        
44426        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44427       'thead',   'tr', 
44428      
44429       'dir', 'menu', 'ol', 'ul', 'dl',
44430        
44431       'embed',  'object'
44432 ];
44433
44434
44435 Roo.HtmlEditorCore.black = [
44436     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44437         'applet', // 
44438         'base',   'basefont', 'bgsound', 'blink',  'body', 
44439         'frame',  'frameset', 'head',    'html',   'ilayer', 
44440         'iframe', 'layer',  'link',     'meta',    'object',   
44441         'script', 'style' ,'title',  'xml' // clean later..
44442 ];
44443 Roo.HtmlEditorCore.clean = [
44444     'script', 'style', 'title', 'xml'
44445 ];
44446 Roo.HtmlEditorCore.remove = [
44447     'font'
44448 ];
44449 // attributes..
44450
44451 Roo.HtmlEditorCore.ablack = [
44452     'on'
44453 ];
44454     
44455 Roo.HtmlEditorCore.aclean = [ 
44456     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44457 ];
44458
44459 // protocols..
44460 Roo.HtmlEditorCore.pwhite= [
44461         'http',  'https',  'mailto'
44462 ];
44463
44464 // white listed style attributes.
44465 Roo.HtmlEditorCore.cwhite= [
44466       //  'text-align', /// default is to allow most things..
44467       
44468          
44469 //        'font-size'//??
44470 ];
44471
44472 // black listed style attributes.
44473 Roo.HtmlEditorCore.cblack= [
44474       //  'font-size' -- this can be set by the project 
44475 ];
44476
44477
44478 Roo.HtmlEditorCore.swapCodes   =[ 
44479     [    8211, "--" ], 
44480     [    8212, "--" ], 
44481     [    8216,  "'" ],  
44482     [    8217, "'" ],  
44483     [    8220, '"' ],  
44484     [    8221, '"' ],  
44485     [    8226, "*" ],  
44486     [    8230, "..." ]
44487 ]; 
44488
44489     //<script type="text/javascript">
44490
44491 /*
44492  * Ext JS Library 1.1.1
44493  * Copyright(c) 2006-2007, Ext JS, LLC.
44494  * Licence LGPL
44495  * 
44496  */
44497  
44498  
44499 Roo.form.HtmlEditor = function(config){
44500     
44501     
44502     
44503     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44504     
44505     if (!this.toolbars) {
44506         this.toolbars = [];
44507     }
44508     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44509     
44510     
44511 };
44512
44513 /**
44514  * @class Roo.form.HtmlEditor
44515  * @extends Roo.form.Field
44516  * Provides a lightweight HTML Editor component.
44517  *
44518  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44519  * 
44520  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44521  * supported by this editor.</b><br/><br/>
44522  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44523  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44524  */
44525 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44526     /**
44527      * @cfg {Boolean} clearUp
44528      */
44529     clearUp : true,
44530       /**
44531      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44532      */
44533     toolbars : false,
44534    
44535      /**
44536      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44537      *                        Roo.resizable.
44538      */
44539     resizable : false,
44540      /**
44541      * @cfg {Number} height (in pixels)
44542      */   
44543     height: 300,
44544    /**
44545      * @cfg {Number} width (in pixels)
44546      */   
44547     width: 500,
44548     
44549     /**
44550      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44551      * 
44552      */
44553     stylesheets: false,
44554     
44555     
44556      /**
44557      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44558      * 
44559      */
44560     cblack: false,
44561     /**
44562      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44563      * 
44564      */
44565     cwhite: false,
44566     
44567      /**
44568      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44569      * 
44570      */
44571     black: false,
44572     /**
44573      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44574      * 
44575      */
44576     white: false,
44577     
44578     // id of frame..
44579     frameId: false,
44580     
44581     // private properties
44582     validationEvent : false,
44583     deferHeight: true,
44584     initialized : false,
44585     activated : false,
44586     
44587     onFocus : Roo.emptyFn,
44588     iframePad:3,
44589     hideMode:'offsets',
44590     
44591     actionMode : 'container', // defaults to hiding it...
44592     
44593     defaultAutoCreate : { // modified by initCompnoent..
44594         tag: "textarea",
44595         style:"width:500px;height:300px;",
44596         autocomplete: "new-password"
44597     },
44598
44599     // private
44600     initComponent : function(){
44601         this.addEvents({
44602             /**
44603              * @event initialize
44604              * Fires when the editor is fully initialized (including the iframe)
44605              * @param {HtmlEditor} this
44606              */
44607             initialize: true,
44608             /**
44609              * @event activate
44610              * Fires when the editor is first receives the focus. Any insertion must wait
44611              * until after this event.
44612              * @param {HtmlEditor} this
44613              */
44614             activate: true,
44615              /**
44616              * @event beforesync
44617              * Fires before the textarea is updated with content from the editor iframe. Return false
44618              * to cancel the sync.
44619              * @param {HtmlEditor} this
44620              * @param {String} html
44621              */
44622             beforesync: true,
44623              /**
44624              * @event beforepush
44625              * Fires before the iframe editor is updated with content from the textarea. Return false
44626              * to cancel the push.
44627              * @param {HtmlEditor} this
44628              * @param {String} html
44629              */
44630             beforepush: true,
44631              /**
44632              * @event sync
44633              * Fires when the textarea is updated with content from the editor iframe.
44634              * @param {HtmlEditor} this
44635              * @param {String} html
44636              */
44637             sync: true,
44638              /**
44639              * @event push
44640              * Fires when the iframe editor is updated with content from the textarea.
44641              * @param {HtmlEditor} this
44642              * @param {String} html
44643              */
44644             push: true,
44645              /**
44646              * @event editmodechange
44647              * Fires when the editor switches edit modes
44648              * @param {HtmlEditor} this
44649              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44650              */
44651             editmodechange: true,
44652             /**
44653              * @event editorevent
44654              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44655              * @param {HtmlEditor} this
44656              */
44657             editorevent: true,
44658             /**
44659              * @event firstfocus
44660              * Fires when on first focus - needed by toolbars..
44661              * @param {HtmlEditor} this
44662              */
44663             firstfocus: true,
44664             /**
44665              * @event autosave
44666              * Auto save the htmlEditor value as a file into Events
44667              * @param {HtmlEditor} this
44668              */
44669             autosave: true,
44670             /**
44671              * @event savedpreview
44672              * preview the saved version of htmlEditor
44673              * @param {HtmlEditor} this
44674              */
44675             savedpreview: true,
44676             
44677             /**
44678             * @event stylesheetsclick
44679             * Fires when press the Sytlesheets button
44680             * @param {Roo.HtmlEditorCore} this
44681             */
44682             stylesheetsclick: true
44683         });
44684         this.defaultAutoCreate =  {
44685             tag: "textarea",
44686             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44687             autocomplete: "new-password"
44688         };
44689     },
44690
44691     /**
44692      * Protected method that will not generally be called directly. It
44693      * is called when the editor creates its toolbar. Override this method if you need to
44694      * add custom toolbar buttons.
44695      * @param {HtmlEditor} editor
44696      */
44697     createToolbar : function(editor){
44698         Roo.log("create toolbars");
44699         if (!editor.toolbars || !editor.toolbars.length) {
44700             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44701         }
44702         
44703         for (var i =0 ; i < editor.toolbars.length;i++) {
44704             editor.toolbars[i] = Roo.factory(
44705                     typeof(editor.toolbars[i]) == 'string' ?
44706                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44707                 Roo.form.HtmlEditor);
44708             editor.toolbars[i].init(editor);
44709         }
44710          
44711         
44712     },
44713
44714      
44715     // private
44716     onRender : function(ct, position)
44717     {
44718         var _t = this;
44719         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44720         
44721         this.wrap = this.el.wrap({
44722             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44723         });
44724         
44725         this.editorcore.onRender(ct, position);
44726          
44727         if (this.resizable) {
44728             this.resizeEl = new Roo.Resizable(this.wrap, {
44729                 pinned : true,
44730                 wrap: true,
44731                 dynamic : true,
44732                 minHeight : this.height,
44733                 height: this.height,
44734                 handles : this.resizable,
44735                 width: this.width,
44736                 listeners : {
44737                     resize : function(r, w, h) {
44738                         _t.onResize(w,h); // -something
44739                     }
44740                 }
44741             });
44742             
44743         }
44744         this.createToolbar(this);
44745        
44746         
44747         if(!this.width){
44748             this.setSize(this.wrap.getSize());
44749         }
44750         if (this.resizeEl) {
44751             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44752             // should trigger onReize..
44753         }
44754         
44755         this.keyNav = new Roo.KeyNav(this.el, {
44756             
44757             "tab" : function(e){
44758                 e.preventDefault();
44759                 
44760                 var value = this.getValue();
44761                 
44762                 var start = this.el.dom.selectionStart;
44763                 var end = this.el.dom.selectionEnd;
44764                 
44765                 if(!e.shiftKey){
44766                     
44767                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44768                     this.el.dom.setSelectionRange(end + 1, end + 1);
44769                     return;
44770                 }
44771                 
44772                 var f = value.substring(0, start).split("\t");
44773                 
44774                 if(f.pop().length != 0){
44775                     return;
44776                 }
44777                 
44778                 this.setValue(f.join("\t") + value.substring(end));
44779                 this.el.dom.setSelectionRange(start - 1, start - 1);
44780                 
44781             },
44782             
44783             "home" : function(e){
44784                 e.preventDefault();
44785                 
44786                 var curr = this.el.dom.selectionStart;
44787                 var lines = this.getValue().split("\n");
44788                 
44789                 if(!lines.length){
44790                     return;
44791                 }
44792                 
44793                 if(e.ctrlKey){
44794                     this.el.dom.setSelectionRange(0, 0);
44795                     return;
44796                 }
44797                 
44798                 var pos = 0;
44799                 
44800                 for (var i = 0; i < lines.length;i++) {
44801                     pos += lines[i].length;
44802                     
44803                     if(i != 0){
44804                         pos += 1;
44805                     }
44806                     
44807                     if(pos < curr){
44808                         continue;
44809                     }
44810                     
44811                     pos -= lines[i].length;
44812                     
44813                     break;
44814                 }
44815                 
44816                 if(!e.shiftKey){
44817                     this.el.dom.setSelectionRange(pos, pos);
44818                     return;
44819                 }
44820                 
44821                 this.el.dom.selectionStart = pos;
44822                 this.el.dom.selectionEnd = curr;
44823             },
44824             
44825             "end" : function(e){
44826                 e.preventDefault();
44827                 
44828                 var curr = this.el.dom.selectionStart;
44829                 var lines = this.getValue().split("\n");
44830                 
44831                 if(!lines.length){
44832                     return;
44833                 }
44834                 
44835                 if(e.ctrlKey){
44836                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44837                     return;
44838                 }
44839                 
44840                 var pos = 0;
44841                 
44842                 for (var i = 0; i < lines.length;i++) {
44843                     
44844                     pos += lines[i].length;
44845                     
44846                     if(i != 0){
44847                         pos += 1;
44848                     }
44849                     
44850                     if(pos < curr){
44851                         continue;
44852                     }
44853                     
44854                     break;
44855                 }
44856                 
44857                 if(!e.shiftKey){
44858                     this.el.dom.setSelectionRange(pos, pos);
44859                     return;
44860                 }
44861                 
44862                 this.el.dom.selectionStart = curr;
44863                 this.el.dom.selectionEnd = pos;
44864             },
44865
44866             scope : this,
44867
44868             doRelay : function(foo, bar, hname){
44869                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44870             },
44871
44872             forceKeyDown: true
44873         });
44874         
44875 //        if(this.autosave && this.w){
44876 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44877 //        }
44878     },
44879
44880     // private
44881     onResize : function(w, h)
44882     {
44883         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44884         var ew = false;
44885         var eh = false;
44886         
44887         if(this.el ){
44888             if(typeof w == 'number'){
44889                 var aw = w - this.wrap.getFrameWidth('lr');
44890                 this.el.setWidth(this.adjustWidth('textarea', aw));
44891                 ew = aw;
44892             }
44893             if(typeof h == 'number'){
44894                 var tbh = 0;
44895                 for (var i =0; i < this.toolbars.length;i++) {
44896                     // fixme - ask toolbars for heights?
44897                     tbh += this.toolbars[i].tb.el.getHeight();
44898                     if (this.toolbars[i].footer) {
44899                         tbh += this.toolbars[i].footer.el.getHeight();
44900                     }
44901                 }
44902                 
44903                 
44904                 
44905                 
44906                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44907                 ah -= 5; // knock a few pixes off for look..
44908 //                Roo.log(ah);
44909                 this.el.setHeight(this.adjustWidth('textarea', ah));
44910                 var eh = ah;
44911             }
44912         }
44913         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44914         this.editorcore.onResize(ew,eh);
44915         
44916     },
44917
44918     /**
44919      * Toggles the editor between standard and source edit mode.
44920      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44921      */
44922     toggleSourceEdit : function(sourceEditMode)
44923     {
44924         this.editorcore.toggleSourceEdit(sourceEditMode);
44925         
44926         if(this.editorcore.sourceEditMode){
44927             Roo.log('editor - showing textarea');
44928             
44929 //            Roo.log('in');
44930 //            Roo.log(this.syncValue());
44931             this.editorcore.syncValue();
44932             this.el.removeClass('x-hidden');
44933             this.el.dom.removeAttribute('tabIndex');
44934             this.el.focus();
44935             
44936             for (var i = 0; i < this.toolbars.length; i++) {
44937                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44938                     this.toolbars[i].tb.hide();
44939                     this.toolbars[i].footer.hide();
44940                 }
44941             }
44942             
44943         }else{
44944             Roo.log('editor - hiding textarea');
44945 //            Roo.log('out')
44946 //            Roo.log(this.pushValue()); 
44947             this.editorcore.pushValue();
44948             
44949             this.el.addClass('x-hidden');
44950             this.el.dom.setAttribute('tabIndex', -1);
44951             
44952             for (var i = 0; i < this.toolbars.length; i++) {
44953                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44954                     this.toolbars[i].tb.show();
44955                     this.toolbars[i].footer.show();
44956                 }
44957             }
44958             
44959             //this.deferFocus();
44960         }
44961         
44962         this.setSize(this.wrap.getSize());
44963         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44964         
44965         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44966     },
44967  
44968     // private (for BoxComponent)
44969     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44970
44971     // private (for BoxComponent)
44972     getResizeEl : function(){
44973         return this.wrap;
44974     },
44975
44976     // private (for BoxComponent)
44977     getPositionEl : function(){
44978         return this.wrap;
44979     },
44980
44981     // private
44982     initEvents : function(){
44983         this.originalValue = this.getValue();
44984     },
44985
44986     /**
44987      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44988      * @method
44989      */
44990     markInvalid : Roo.emptyFn,
44991     /**
44992      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44993      * @method
44994      */
44995     clearInvalid : Roo.emptyFn,
44996
44997     setValue : function(v){
44998         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44999         this.editorcore.pushValue();
45000     },
45001
45002      
45003     // private
45004     deferFocus : function(){
45005         this.focus.defer(10, this);
45006     },
45007
45008     // doc'ed in Field
45009     focus : function(){
45010         this.editorcore.focus();
45011         
45012     },
45013       
45014
45015     // private
45016     onDestroy : function(){
45017         
45018         
45019         
45020         if(this.rendered){
45021             
45022             for (var i =0; i < this.toolbars.length;i++) {
45023                 // fixme - ask toolbars for heights?
45024                 this.toolbars[i].onDestroy();
45025             }
45026             
45027             this.wrap.dom.innerHTML = '';
45028             this.wrap.remove();
45029         }
45030     },
45031
45032     // private
45033     onFirstFocus : function(){
45034         //Roo.log("onFirstFocus");
45035         this.editorcore.onFirstFocus();
45036          for (var i =0; i < this.toolbars.length;i++) {
45037             this.toolbars[i].onFirstFocus();
45038         }
45039         
45040     },
45041     
45042     // private
45043     syncValue : function()
45044     {
45045         this.editorcore.syncValue();
45046     },
45047     
45048     pushValue : function()
45049     {
45050         this.editorcore.pushValue();
45051     },
45052     
45053     setStylesheets : function(stylesheets)
45054     {
45055         this.editorcore.setStylesheets(stylesheets);
45056     },
45057     
45058     removeStylesheets : function()
45059     {
45060         this.editorcore.removeStylesheets();
45061     }
45062      
45063     
45064     // hide stuff that is not compatible
45065     /**
45066      * @event blur
45067      * @hide
45068      */
45069     /**
45070      * @event change
45071      * @hide
45072      */
45073     /**
45074      * @event focus
45075      * @hide
45076      */
45077     /**
45078      * @event specialkey
45079      * @hide
45080      */
45081     /**
45082      * @cfg {String} fieldClass @hide
45083      */
45084     /**
45085      * @cfg {String} focusClass @hide
45086      */
45087     /**
45088      * @cfg {String} autoCreate @hide
45089      */
45090     /**
45091      * @cfg {String} inputType @hide
45092      */
45093     /**
45094      * @cfg {String} invalidClass @hide
45095      */
45096     /**
45097      * @cfg {String} invalidText @hide
45098      */
45099     /**
45100      * @cfg {String} msgFx @hide
45101      */
45102     /**
45103      * @cfg {String} validateOnBlur @hide
45104      */
45105 });
45106  
45107     // <script type="text/javascript">
45108 /*
45109  * Based on
45110  * Ext JS Library 1.1.1
45111  * Copyright(c) 2006-2007, Ext JS, LLC.
45112  *  
45113  
45114  */
45115
45116 /**
45117  * @class Roo.form.HtmlEditorToolbar1
45118  * Basic Toolbar
45119  * 
45120  * Usage:
45121  *
45122  new Roo.form.HtmlEditor({
45123     ....
45124     toolbars : [
45125         new Roo.form.HtmlEditorToolbar1({
45126             disable : { fonts: 1 , format: 1, ..., ... , ...],
45127             btns : [ .... ]
45128         })
45129     }
45130      
45131  * 
45132  * @cfg {Object} disable List of elements to disable..
45133  * @cfg {Array} btns List of additional buttons.
45134  * 
45135  * 
45136  * NEEDS Extra CSS? 
45137  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45138  */
45139  
45140 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45141 {
45142     
45143     Roo.apply(this, config);
45144     
45145     // default disabled, based on 'good practice'..
45146     this.disable = this.disable || {};
45147     Roo.applyIf(this.disable, {
45148         fontSize : true,
45149         colors : true,
45150         specialElements : true
45151     });
45152     
45153     
45154     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45155     // dont call parent... till later.
45156 }
45157
45158 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45159     
45160     tb: false,
45161     
45162     rendered: false,
45163     
45164     editor : false,
45165     editorcore : false,
45166     /**
45167      * @cfg {Object} disable  List of toolbar elements to disable
45168          
45169      */
45170     disable : false,
45171     
45172     
45173      /**
45174      * @cfg {String} createLinkText The default text for the create link prompt
45175      */
45176     createLinkText : 'Please enter the URL for the link:',
45177     /**
45178      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45179      */
45180     defaultLinkValue : 'http:/'+'/',
45181    
45182     
45183       /**
45184      * @cfg {Array} fontFamilies An array of available font families
45185      */
45186     fontFamilies : [
45187         'Arial',
45188         'Courier New',
45189         'Tahoma',
45190         'Times New Roman',
45191         'Verdana'
45192     ],
45193     
45194     specialChars : [
45195            "&#169;",
45196           "&#174;",     
45197           "&#8482;",    
45198           "&#163;" ,    
45199          // "&#8212;",    
45200           "&#8230;",    
45201           "&#247;" ,    
45202         //  "&#225;" ,     ?? a acute?
45203            "&#8364;"    , //Euro
45204        //   "&#8220;"    ,
45205         //  "&#8221;"    ,
45206         //  "&#8226;"    ,
45207           "&#176;"  //   , // degrees
45208
45209          // "&#233;"     , // e ecute
45210          // "&#250;"     , // u ecute?
45211     ],
45212     
45213     specialElements : [
45214         {
45215             text: "Insert Table",
45216             xtype: 'MenuItem',
45217             xns : Roo.Menu,
45218             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45219                 
45220         },
45221         {    
45222             text: "Insert Image",
45223             xtype: 'MenuItem',
45224             xns : Roo.Menu,
45225             ihtml : '<img src="about:blank"/>'
45226             
45227         }
45228         
45229          
45230     ],
45231     
45232     
45233     inputElements : [ 
45234             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45235             "input:submit", "input:button", "select", "textarea", "label" ],
45236     formats : [
45237         ["p"] ,  
45238         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45239         ["pre"],[ "code"], 
45240         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45241         ['div'],['span']
45242     ],
45243     
45244     cleanStyles : [
45245         "font-size"
45246     ],
45247      /**
45248      * @cfg {String} defaultFont default font to use.
45249      */
45250     defaultFont: 'tahoma',
45251    
45252     fontSelect : false,
45253     
45254     
45255     formatCombo : false,
45256     
45257     init : function(editor)
45258     {
45259         this.editor = editor;
45260         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45261         var editorcore = this.editorcore;
45262         
45263         var _t = this;
45264         
45265         var fid = editorcore.frameId;
45266         var etb = this;
45267         function btn(id, toggle, handler){
45268             var xid = fid + '-'+ id ;
45269             return {
45270                 id : xid,
45271                 cmd : id,
45272                 cls : 'x-btn-icon x-edit-'+id,
45273                 enableToggle:toggle !== false,
45274                 scope: _t, // was editor...
45275                 handler:handler||_t.relayBtnCmd,
45276                 clickEvent:'mousedown',
45277                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45278                 tabIndex:-1
45279             };
45280         }
45281         
45282         
45283         
45284         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45285         this.tb = tb;
45286          // stop form submits
45287         tb.el.on('click', function(e){
45288             e.preventDefault(); // what does this do?
45289         });
45290
45291         if(!this.disable.font) { // && !Roo.isSafari){
45292             /* why no safari for fonts 
45293             editor.fontSelect = tb.el.createChild({
45294                 tag:'select',
45295                 tabIndex: -1,
45296                 cls:'x-font-select',
45297                 html: this.createFontOptions()
45298             });
45299             
45300             editor.fontSelect.on('change', function(){
45301                 var font = editor.fontSelect.dom.value;
45302                 editor.relayCmd('fontname', font);
45303                 editor.deferFocus();
45304             }, editor);
45305             
45306             tb.add(
45307                 editor.fontSelect.dom,
45308                 '-'
45309             );
45310             */
45311             
45312         };
45313         if(!this.disable.formats){
45314             this.formatCombo = new Roo.form.ComboBox({
45315                 store: new Roo.data.SimpleStore({
45316                     id : 'tag',
45317                     fields: ['tag'],
45318                     data : this.formats // from states.js
45319                 }),
45320                 blockFocus : true,
45321                 name : '',
45322                 //autoCreate : {tag: "div",  size: "20"},
45323                 displayField:'tag',
45324                 typeAhead: false,
45325                 mode: 'local',
45326                 editable : false,
45327                 triggerAction: 'all',
45328                 emptyText:'Add tag',
45329                 selectOnFocus:true,
45330                 width:135,
45331                 listeners : {
45332                     'select': function(c, r, i) {
45333                         editorcore.insertTag(r.get('tag'));
45334                         editor.focus();
45335                     }
45336                 }
45337
45338             });
45339             tb.addField(this.formatCombo);
45340             
45341         }
45342         
45343         if(!this.disable.format){
45344             tb.add(
45345                 btn('bold'),
45346                 btn('italic'),
45347                 btn('underline'),
45348                 btn('strikethrough')
45349             );
45350         };
45351         if(!this.disable.fontSize){
45352             tb.add(
45353                 '-',
45354                 
45355                 
45356                 btn('increasefontsize', false, editorcore.adjustFont),
45357                 btn('decreasefontsize', false, editorcore.adjustFont)
45358             );
45359         };
45360         
45361         
45362         if(!this.disable.colors){
45363             tb.add(
45364                 '-', {
45365                     id:editorcore.frameId +'-forecolor',
45366                     cls:'x-btn-icon x-edit-forecolor',
45367                     clickEvent:'mousedown',
45368                     tooltip: this.buttonTips['forecolor'] || undefined,
45369                     tabIndex:-1,
45370                     menu : new Roo.menu.ColorMenu({
45371                         allowReselect: true,
45372                         focus: Roo.emptyFn,
45373                         value:'000000',
45374                         plain:true,
45375                         selectHandler: function(cp, color){
45376                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45377                             editor.deferFocus();
45378                         },
45379                         scope: editorcore,
45380                         clickEvent:'mousedown'
45381                     })
45382                 }, {
45383                     id:editorcore.frameId +'backcolor',
45384                     cls:'x-btn-icon x-edit-backcolor',
45385                     clickEvent:'mousedown',
45386                     tooltip: this.buttonTips['backcolor'] || undefined,
45387                     tabIndex:-1,
45388                     menu : new Roo.menu.ColorMenu({
45389                         focus: Roo.emptyFn,
45390                         value:'FFFFFF',
45391                         plain:true,
45392                         allowReselect: true,
45393                         selectHandler: function(cp, color){
45394                             if(Roo.isGecko){
45395                                 editorcore.execCmd('useCSS', false);
45396                                 editorcore.execCmd('hilitecolor', color);
45397                                 editorcore.execCmd('useCSS', true);
45398                                 editor.deferFocus();
45399                             }else{
45400                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45401                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45402                                 editor.deferFocus();
45403                             }
45404                         },
45405                         scope:editorcore,
45406                         clickEvent:'mousedown'
45407                     })
45408                 }
45409             );
45410         };
45411         // now add all the items...
45412         
45413
45414         if(!this.disable.alignments){
45415             tb.add(
45416                 '-',
45417                 btn('justifyleft'),
45418                 btn('justifycenter'),
45419                 btn('justifyright')
45420             );
45421         };
45422
45423         //if(!Roo.isSafari){
45424             if(!this.disable.links){
45425                 tb.add(
45426                     '-',
45427                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45428                 );
45429             };
45430
45431             if(!this.disable.lists){
45432                 tb.add(
45433                     '-',
45434                     btn('insertorderedlist'),
45435                     btn('insertunorderedlist')
45436                 );
45437             }
45438             if(!this.disable.sourceEdit){
45439                 tb.add(
45440                     '-',
45441                     btn('sourceedit', true, function(btn){
45442                         this.toggleSourceEdit(btn.pressed);
45443                     })
45444                 );
45445             }
45446         //}
45447         
45448         var smenu = { };
45449         // special menu.. - needs to be tidied up..
45450         if (!this.disable.special) {
45451             smenu = {
45452                 text: "&#169;",
45453                 cls: 'x-edit-none',
45454                 
45455                 menu : {
45456                     items : []
45457                 }
45458             };
45459             for (var i =0; i < this.specialChars.length; i++) {
45460                 smenu.menu.items.push({
45461                     
45462                     html: this.specialChars[i],
45463                     handler: function(a,b) {
45464                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45465                         //editor.insertAtCursor(a.html);
45466                         
45467                     },
45468                     tabIndex:-1
45469                 });
45470             }
45471             
45472             
45473             tb.add(smenu);
45474             
45475             
45476         }
45477         
45478         var cmenu = { };
45479         if (!this.disable.cleanStyles) {
45480             cmenu = {
45481                 cls: 'x-btn-icon x-btn-clear',
45482                 
45483                 menu : {
45484                     items : []
45485                 }
45486             };
45487             for (var i =0; i < this.cleanStyles.length; i++) {
45488                 cmenu.menu.items.push({
45489                     actiontype : this.cleanStyles[i],
45490                     html: 'Remove ' + this.cleanStyles[i],
45491                     handler: function(a,b) {
45492 //                        Roo.log(a);
45493 //                        Roo.log(b);
45494                         var c = Roo.get(editorcore.doc.body);
45495                         c.select('[style]').each(function(s) {
45496                             s.dom.style.removeProperty(a.actiontype);
45497                         });
45498                         editorcore.syncValue();
45499                     },
45500                     tabIndex:-1
45501                 });
45502             }
45503              cmenu.menu.items.push({
45504                 actiontype : 'tablewidths',
45505                 html: 'Remove Table Widths',
45506                 handler: function(a,b) {
45507                     editorcore.cleanTableWidths();
45508                     editorcore.syncValue();
45509                 },
45510                 tabIndex:-1
45511             });
45512             cmenu.menu.items.push({
45513                 actiontype : 'word',
45514                 html: 'Remove MS Word Formating',
45515                 handler: function(a,b) {
45516                     editorcore.cleanWord();
45517                     editorcore.syncValue();
45518                 },
45519                 tabIndex:-1
45520             });
45521             
45522             cmenu.menu.items.push({
45523                 actiontype : 'all',
45524                 html: 'Remove All Styles',
45525                 handler: function(a,b) {
45526                     
45527                     var c = Roo.get(editorcore.doc.body);
45528                     c.select('[style]').each(function(s) {
45529                         s.dom.removeAttribute('style');
45530                     });
45531                     editorcore.syncValue();
45532                 },
45533                 tabIndex:-1
45534             });
45535             
45536             cmenu.menu.items.push({
45537                 actiontype : 'all',
45538                 html: 'Remove All CSS Classes',
45539                 handler: function(a,b) {
45540                     
45541                     var c = Roo.get(editorcore.doc.body);
45542                     c.select('[class]').each(function(s) {
45543                         s.dom.className = '';
45544                     });
45545                     editorcore.syncValue();
45546                 },
45547                 tabIndex:-1
45548             });
45549             
45550              cmenu.menu.items.push({
45551                 actiontype : 'tidy',
45552                 html: 'Tidy HTML Source',
45553                 handler: function(a,b) {
45554                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45555                     editorcore.syncValue();
45556                 },
45557                 tabIndex:-1
45558             });
45559             
45560             
45561             tb.add(cmenu);
45562         }
45563          
45564         if (!this.disable.specialElements) {
45565             var semenu = {
45566                 text: "Other;",
45567                 cls: 'x-edit-none',
45568                 menu : {
45569                     items : []
45570                 }
45571             };
45572             for (var i =0; i < this.specialElements.length; i++) {
45573                 semenu.menu.items.push(
45574                     Roo.apply({ 
45575                         handler: function(a,b) {
45576                             editor.insertAtCursor(this.ihtml);
45577                         }
45578                     }, this.specialElements[i])
45579                 );
45580                     
45581             }
45582             
45583             tb.add(semenu);
45584             
45585             
45586         }
45587          
45588         
45589         if (this.btns) {
45590             for(var i =0; i< this.btns.length;i++) {
45591                 var b = Roo.factory(this.btns[i],Roo.form);
45592                 b.cls =  'x-edit-none';
45593                 
45594                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45595                     b.cls += ' x-init-enable';
45596                 }
45597                 
45598                 b.scope = editorcore;
45599                 tb.add(b);
45600             }
45601         
45602         }
45603         
45604         
45605         
45606         // disable everything...
45607         
45608         this.tb.items.each(function(item){
45609             
45610            if(
45611                 item.id != editorcore.frameId+ '-sourceedit' && 
45612                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45613             ){
45614                 
45615                 item.disable();
45616             }
45617         });
45618         this.rendered = true;
45619         
45620         // the all the btns;
45621         editor.on('editorevent', this.updateToolbar, this);
45622         // other toolbars need to implement this..
45623         //editor.on('editmodechange', this.updateToolbar, this);
45624     },
45625     
45626     
45627     relayBtnCmd : function(btn) {
45628         this.editorcore.relayCmd(btn.cmd);
45629     },
45630     // private used internally
45631     createLink : function(){
45632         Roo.log("create link?");
45633         var url = prompt(this.createLinkText, this.defaultLinkValue);
45634         if(url && url != 'http:/'+'/'){
45635             this.editorcore.relayCmd('createlink', url);
45636         }
45637     },
45638
45639     
45640     /**
45641      * Protected method that will not generally be called directly. It triggers
45642      * a toolbar update by reading the markup state of the current selection in the editor.
45643      */
45644     updateToolbar: function(){
45645
45646         if(!this.editorcore.activated){
45647             this.editor.onFirstFocus();
45648             return;
45649         }
45650
45651         var btns = this.tb.items.map, 
45652             doc = this.editorcore.doc,
45653             frameId = this.editorcore.frameId;
45654
45655         if(!this.disable.font && !Roo.isSafari){
45656             /*
45657             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45658             if(name != this.fontSelect.dom.value){
45659                 this.fontSelect.dom.value = name;
45660             }
45661             */
45662         }
45663         if(!this.disable.format){
45664             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45665             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45666             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45667             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45668         }
45669         if(!this.disable.alignments){
45670             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45671             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45672             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45673         }
45674         if(!Roo.isSafari && !this.disable.lists){
45675             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45676             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45677         }
45678         
45679         var ans = this.editorcore.getAllAncestors();
45680         if (this.formatCombo) {
45681             
45682             
45683             var store = this.formatCombo.store;
45684             this.formatCombo.setValue("");
45685             for (var i =0; i < ans.length;i++) {
45686                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45687                     // select it..
45688                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45689                     break;
45690                 }
45691             }
45692         }
45693         
45694         
45695         
45696         // hides menus... - so this cant be on a menu...
45697         Roo.menu.MenuMgr.hideAll();
45698
45699         //this.editorsyncValue();
45700     },
45701    
45702     
45703     createFontOptions : function(){
45704         var buf = [], fs = this.fontFamilies, ff, lc;
45705         
45706         
45707         
45708         for(var i = 0, len = fs.length; i< len; i++){
45709             ff = fs[i];
45710             lc = ff.toLowerCase();
45711             buf.push(
45712                 '<option value="',lc,'" style="font-family:',ff,';"',
45713                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45714                     ff,
45715                 '</option>'
45716             );
45717         }
45718         return buf.join('');
45719     },
45720     
45721     toggleSourceEdit : function(sourceEditMode){
45722         
45723         Roo.log("toolbar toogle");
45724         if(sourceEditMode === undefined){
45725             sourceEditMode = !this.sourceEditMode;
45726         }
45727         this.sourceEditMode = sourceEditMode === true;
45728         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45729         // just toggle the button?
45730         if(btn.pressed !== this.sourceEditMode){
45731             btn.toggle(this.sourceEditMode);
45732             return;
45733         }
45734         
45735         if(sourceEditMode){
45736             Roo.log("disabling buttons");
45737             this.tb.items.each(function(item){
45738                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45739                     item.disable();
45740                 }
45741             });
45742           
45743         }else{
45744             Roo.log("enabling buttons");
45745             if(this.editorcore.initialized){
45746                 this.tb.items.each(function(item){
45747                     item.enable();
45748                 });
45749             }
45750             
45751         }
45752         Roo.log("calling toggole on editor");
45753         // tell the editor that it's been pressed..
45754         this.editor.toggleSourceEdit(sourceEditMode);
45755        
45756     },
45757      /**
45758      * Object collection of toolbar tooltips for the buttons in the editor. The key
45759      * is the command id associated with that button and the value is a valid QuickTips object.
45760      * For example:
45761 <pre><code>
45762 {
45763     bold : {
45764         title: 'Bold (Ctrl+B)',
45765         text: 'Make the selected text bold.',
45766         cls: 'x-html-editor-tip'
45767     },
45768     italic : {
45769         title: 'Italic (Ctrl+I)',
45770         text: 'Make the selected text italic.',
45771         cls: 'x-html-editor-tip'
45772     },
45773     ...
45774 </code></pre>
45775     * @type Object
45776      */
45777     buttonTips : {
45778         bold : {
45779             title: 'Bold (Ctrl+B)',
45780             text: 'Make the selected text bold.',
45781             cls: 'x-html-editor-tip'
45782         },
45783         italic : {
45784             title: 'Italic (Ctrl+I)',
45785             text: 'Make the selected text italic.',
45786             cls: 'x-html-editor-tip'
45787         },
45788         underline : {
45789             title: 'Underline (Ctrl+U)',
45790             text: 'Underline the selected text.',
45791             cls: 'x-html-editor-tip'
45792         },
45793         strikethrough : {
45794             title: 'Strikethrough',
45795             text: 'Strikethrough the selected text.',
45796             cls: 'x-html-editor-tip'
45797         },
45798         increasefontsize : {
45799             title: 'Grow Text',
45800             text: 'Increase the font size.',
45801             cls: 'x-html-editor-tip'
45802         },
45803         decreasefontsize : {
45804             title: 'Shrink Text',
45805             text: 'Decrease the font size.',
45806             cls: 'x-html-editor-tip'
45807         },
45808         backcolor : {
45809             title: 'Text Highlight Color',
45810             text: 'Change the background color of the selected text.',
45811             cls: 'x-html-editor-tip'
45812         },
45813         forecolor : {
45814             title: 'Font Color',
45815             text: 'Change the color of the selected text.',
45816             cls: 'x-html-editor-tip'
45817         },
45818         justifyleft : {
45819             title: 'Align Text Left',
45820             text: 'Align text to the left.',
45821             cls: 'x-html-editor-tip'
45822         },
45823         justifycenter : {
45824             title: 'Center Text',
45825             text: 'Center text in the editor.',
45826             cls: 'x-html-editor-tip'
45827         },
45828         justifyright : {
45829             title: 'Align Text Right',
45830             text: 'Align text to the right.',
45831             cls: 'x-html-editor-tip'
45832         },
45833         insertunorderedlist : {
45834             title: 'Bullet List',
45835             text: 'Start a bulleted list.',
45836             cls: 'x-html-editor-tip'
45837         },
45838         insertorderedlist : {
45839             title: 'Numbered List',
45840             text: 'Start a numbered list.',
45841             cls: 'x-html-editor-tip'
45842         },
45843         createlink : {
45844             title: 'Hyperlink',
45845             text: 'Make the selected text a hyperlink.',
45846             cls: 'x-html-editor-tip'
45847         },
45848         sourceedit : {
45849             title: 'Source Edit',
45850             text: 'Switch to source editing mode.',
45851             cls: 'x-html-editor-tip'
45852         }
45853     },
45854     // private
45855     onDestroy : function(){
45856         if(this.rendered){
45857             
45858             this.tb.items.each(function(item){
45859                 if(item.menu){
45860                     item.menu.removeAll();
45861                     if(item.menu.el){
45862                         item.menu.el.destroy();
45863                     }
45864                 }
45865                 item.destroy();
45866             });
45867              
45868         }
45869     },
45870     onFirstFocus: function() {
45871         this.tb.items.each(function(item){
45872            item.enable();
45873         });
45874     }
45875 });
45876
45877
45878
45879
45880 // <script type="text/javascript">
45881 /*
45882  * Based on
45883  * Ext JS Library 1.1.1
45884  * Copyright(c) 2006-2007, Ext JS, LLC.
45885  *  
45886  
45887  */
45888
45889  
45890 /**
45891  * @class Roo.form.HtmlEditor.ToolbarContext
45892  * Context Toolbar
45893  * 
45894  * Usage:
45895  *
45896  new Roo.form.HtmlEditor({
45897     ....
45898     toolbars : [
45899         { xtype: 'ToolbarStandard', styles : {} }
45900         { xtype: 'ToolbarContext', disable : {} }
45901     ]
45902 })
45903
45904      
45905  * 
45906  * @config : {Object} disable List of elements to disable.. (not done yet.)
45907  * @config : {Object} styles  Map of styles available.
45908  * 
45909  */
45910
45911 Roo.form.HtmlEditor.ToolbarContext = function(config)
45912 {
45913     
45914     Roo.apply(this, config);
45915     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45916     // dont call parent... till later.
45917     this.styles = this.styles || {};
45918 }
45919
45920  
45921
45922 Roo.form.HtmlEditor.ToolbarContext.types = {
45923     'IMG' : {
45924         width : {
45925             title: "Width",
45926             width: 40
45927         },
45928         height:  {
45929             title: "Height",
45930             width: 40
45931         },
45932         align: {
45933             title: "Align",
45934             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45935             width : 80
45936             
45937         },
45938         border: {
45939             title: "Border",
45940             width: 40
45941         },
45942         alt: {
45943             title: "Alt",
45944             width: 120
45945         },
45946         src : {
45947             title: "Src",
45948             width: 220
45949         }
45950         
45951     },
45952     'A' : {
45953         name : {
45954             title: "Name",
45955             width: 50
45956         },
45957         target:  {
45958             title: "Target",
45959             width: 120
45960         },
45961         href:  {
45962             title: "Href",
45963             width: 220
45964         } // border?
45965         
45966     },
45967     'TABLE' : {
45968         rows : {
45969             title: "Rows",
45970             width: 20
45971         },
45972         cols : {
45973             title: "Cols",
45974             width: 20
45975         },
45976         width : {
45977             title: "Width",
45978             width: 40
45979         },
45980         height : {
45981             title: "Height",
45982             width: 40
45983         },
45984         border : {
45985             title: "Border",
45986             width: 20
45987         }
45988     },
45989     'TD' : {
45990         width : {
45991             title: "Width",
45992             width: 40
45993         },
45994         height : {
45995             title: "Height",
45996             width: 40
45997         },   
45998         align: {
45999             title: "Align",
46000             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46001             width: 80
46002         },
46003         valign: {
46004             title: "Valign",
46005             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46006             width: 80
46007         },
46008         colspan: {
46009             title: "Colspan",
46010             width: 20
46011             
46012         },
46013          'font-family'  : {
46014             title : "Font",
46015             style : 'fontFamily',
46016             displayField: 'display',
46017             optname : 'font-family',
46018             width: 140
46019         }
46020     },
46021     'INPUT' : {
46022         name : {
46023             title: "name",
46024             width: 120
46025         },
46026         value : {
46027             title: "Value",
46028             width: 120
46029         },
46030         width : {
46031             title: "Width",
46032             width: 40
46033         }
46034     },
46035     'LABEL' : {
46036         'for' : {
46037             title: "For",
46038             width: 120
46039         }
46040     },
46041     'TEXTAREA' : {
46042           name : {
46043             title: "name",
46044             width: 120
46045         },
46046         rows : {
46047             title: "Rows",
46048             width: 20
46049         },
46050         cols : {
46051             title: "Cols",
46052             width: 20
46053         }
46054     },
46055     'SELECT' : {
46056         name : {
46057             title: "name",
46058             width: 120
46059         },
46060         selectoptions : {
46061             title: "Options",
46062             width: 200
46063         }
46064     },
46065     
46066     // should we really allow this??
46067     // should this just be 
46068     'BODY' : {
46069         title : {
46070             title: "Title",
46071             width: 200,
46072             disabled : true
46073         }
46074     },
46075     'SPAN' : {
46076         'font-family'  : {
46077             title : "Font",
46078             style : 'fontFamily',
46079             displayField: 'display',
46080             optname : 'font-family',
46081             width: 140
46082         }
46083     },
46084     'DIV' : {
46085         'font-family'  : {
46086             title : "Font",
46087             style : 'fontFamily',
46088             displayField: 'display',
46089             optname : 'font-family',
46090             width: 140
46091         }
46092     },
46093      'P' : {
46094         'font-family'  : {
46095             title : "Font",
46096             style : 'fontFamily',
46097             displayField: 'display',
46098             optname : 'font-family',
46099             width: 140
46100         }
46101     },
46102     
46103     '*' : {
46104         // empty..
46105     }
46106
46107 };
46108
46109 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46110 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46111
46112 Roo.form.HtmlEditor.ToolbarContext.options = {
46113         'font-family'  : [ 
46114                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46115                 [ 'Courier New', 'Courier New'],
46116                 [ 'Tahoma', 'Tahoma'],
46117                 [ 'Times New Roman,serif', 'Times'],
46118                 [ 'Verdana','Verdana' ]
46119         ]
46120 };
46121
46122 // fixme - these need to be configurable..
46123  
46124
46125 //Roo.form.HtmlEditor.ToolbarContext.types
46126
46127
46128 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46129     
46130     tb: false,
46131     
46132     rendered: false,
46133     
46134     editor : false,
46135     editorcore : false,
46136     /**
46137      * @cfg {Object} disable  List of toolbar elements to disable
46138          
46139      */
46140     disable : false,
46141     /**
46142      * @cfg {Object} styles List of styles 
46143      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46144      *
46145      * These must be defined in the page, so they get rendered correctly..
46146      * .headline { }
46147      * TD.underline { }
46148      * 
46149      */
46150     styles : false,
46151     
46152     options: false,
46153     
46154     toolbars : false,
46155     
46156     init : function(editor)
46157     {
46158         this.editor = editor;
46159         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46160         var editorcore = this.editorcore;
46161         
46162         var fid = editorcore.frameId;
46163         var etb = this;
46164         function btn(id, toggle, handler){
46165             var xid = fid + '-'+ id ;
46166             return {
46167                 id : xid,
46168                 cmd : id,
46169                 cls : 'x-btn-icon x-edit-'+id,
46170                 enableToggle:toggle !== false,
46171                 scope: editorcore, // was editor...
46172                 handler:handler||editorcore.relayBtnCmd,
46173                 clickEvent:'mousedown',
46174                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46175                 tabIndex:-1
46176             };
46177         }
46178         // create a new element.
46179         var wdiv = editor.wrap.createChild({
46180                 tag: 'div'
46181             }, editor.wrap.dom.firstChild.nextSibling, true);
46182         
46183         // can we do this more than once??
46184         
46185          // stop form submits
46186       
46187  
46188         // disable everything...
46189         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46190         this.toolbars = {};
46191            
46192         for (var i in  ty) {
46193           
46194             this.toolbars[i] = this.buildToolbar(ty[i],i);
46195         }
46196         this.tb = this.toolbars.BODY;
46197         this.tb.el.show();
46198         this.buildFooter();
46199         this.footer.show();
46200         editor.on('hide', function( ) { this.footer.hide() }, this);
46201         editor.on('show', function( ) { this.footer.show() }, this);
46202         
46203          
46204         this.rendered = true;
46205         
46206         // the all the btns;
46207         editor.on('editorevent', this.updateToolbar, this);
46208         // other toolbars need to implement this..
46209         //editor.on('editmodechange', this.updateToolbar, this);
46210     },
46211     
46212     
46213     
46214     /**
46215      * Protected method that will not generally be called directly. It triggers
46216      * a toolbar update by reading the markup state of the current selection in the editor.
46217      *
46218      * Note you can force an update by calling on('editorevent', scope, false)
46219      */
46220     updateToolbar: function(editor,ev,sel){
46221
46222         //Roo.log(ev);
46223         // capture mouse up - this is handy for selecting images..
46224         // perhaps should go somewhere else...
46225         if(!this.editorcore.activated){
46226              this.editor.onFirstFocus();
46227             return;
46228         }
46229         
46230         
46231         
46232         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46233         // selectNode - might want to handle IE?
46234         if (ev &&
46235             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46236             ev.target && ev.target.tagName == 'IMG') {
46237             // they have click on an image...
46238             // let's see if we can change the selection...
46239             sel = ev.target;
46240          
46241               var nodeRange = sel.ownerDocument.createRange();
46242             try {
46243                 nodeRange.selectNode(sel);
46244             } catch (e) {
46245                 nodeRange.selectNodeContents(sel);
46246             }
46247             //nodeRange.collapse(true);
46248             var s = this.editorcore.win.getSelection();
46249             s.removeAllRanges();
46250             s.addRange(nodeRange);
46251         }  
46252         
46253       
46254         var updateFooter = sel ? false : true;
46255         
46256         
46257         var ans = this.editorcore.getAllAncestors();
46258         
46259         // pick
46260         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46261         
46262         if (!sel) { 
46263             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46264             sel = sel ? sel : this.editorcore.doc.body;
46265             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46266             
46267         }
46268         // pick a menu that exists..
46269         var tn = sel.tagName.toUpperCase();
46270         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46271         
46272         tn = sel.tagName.toUpperCase();
46273         
46274         var lastSel = this.tb.selectedNode;
46275         
46276         this.tb.selectedNode = sel;
46277         
46278         // if current menu does not match..
46279         
46280         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46281                 
46282             this.tb.el.hide();
46283             ///console.log("show: " + tn);
46284             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46285             this.tb.el.show();
46286             // update name
46287             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46288             
46289             
46290             // update attributes
46291             if (this.tb.fields) {
46292                 this.tb.fields.each(function(e) {
46293                     if (e.stylename) {
46294                         e.setValue(sel.style[e.stylename]);
46295                         return;
46296                     } 
46297                    e.setValue(sel.getAttribute(e.attrname));
46298                 });
46299             }
46300             
46301             var hasStyles = false;
46302             for(var i in this.styles) {
46303                 hasStyles = true;
46304                 break;
46305             }
46306             
46307             // update styles
46308             if (hasStyles) { 
46309                 var st = this.tb.fields.item(0);
46310                 
46311                 st.store.removeAll();
46312                
46313                 
46314                 var cn = sel.className.split(/\s+/);
46315                 
46316                 var avs = [];
46317                 if (this.styles['*']) {
46318                     
46319                     Roo.each(this.styles['*'], function(v) {
46320                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46321                     });
46322                 }
46323                 if (this.styles[tn]) { 
46324                     Roo.each(this.styles[tn], function(v) {
46325                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46326                     });
46327                 }
46328                 
46329                 st.store.loadData(avs);
46330                 st.collapse();
46331                 st.setValue(cn);
46332             }
46333             // flag our selected Node.
46334             this.tb.selectedNode = sel;
46335            
46336            
46337             Roo.menu.MenuMgr.hideAll();
46338
46339         }
46340         
46341         if (!updateFooter) {
46342             //this.footDisp.dom.innerHTML = ''; 
46343             return;
46344         }
46345         // update the footer
46346         //
46347         var html = '';
46348         
46349         this.footerEls = ans.reverse();
46350         Roo.each(this.footerEls, function(a,i) {
46351             if (!a) { return; }
46352             html += html.length ? ' &gt; '  :  '';
46353             
46354             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46355             
46356         });
46357        
46358         // 
46359         var sz = this.footDisp.up('td').getSize();
46360         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46361         this.footDisp.dom.style.marginLeft = '5px';
46362         
46363         this.footDisp.dom.style.overflow = 'hidden';
46364         
46365         this.footDisp.dom.innerHTML = html;
46366             
46367         //this.editorsyncValue();
46368     },
46369      
46370     
46371    
46372        
46373     // private
46374     onDestroy : function(){
46375         if(this.rendered){
46376             
46377             this.tb.items.each(function(item){
46378                 if(item.menu){
46379                     item.menu.removeAll();
46380                     if(item.menu.el){
46381                         item.menu.el.destroy();
46382                     }
46383                 }
46384                 item.destroy();
46385             });
46386              
46387         }
46388     },
46389     onFirstFocus: function() {
46390         // need to do this for all the toolbars..
46391         this.tb.items.each(function(item){
46392            item.enable();
46393         });
46394     },
46395     buildToolbar: function(tlist, nm)
46396     {
46397         var editor = this.editor;
46398         var editorcore = this.editorcore;
46399          // create a new element.
46400         var wdiv = editor.wrap.createChild({
46401                 tag: 'div'
46402             }, editor.wrap.dom.firstChild.nextSibling, true);
46403         
46404        
46405         var tb = new Roo.Toolbar(wdiv);
46406         // add the name..
46407         
46408         tb.add(nm+ ":&nbsp;");
46409         
46410         var styles = [];
46411         for(var i in this.styles) {
46412             styles.push(i);
46413         }
46414         
46415         // styles...
46416         if (styles && styles.length) {
46417             
46418             // this needs a multi-select checkbox...
46419             tb.addField( new Roo.form.ComboBox({
46420                 store: new Roo.data.SimpleStore({
46421                     id : 'val',
46422                     fields: ['val', 'selected'],
46423                     data : [] 
46424                 }),
46425                 name : '-roo-edit-className',
46426                 attrname : 'className',
46427                 displayField: 'val',
46428                 typeAhead: false,
46429                 mode: 'local',
46430                 editable : false,
46431                 triggerAction: 'all',
46432                 emptyText:'Select Style',
46433                 selectOnFocus:true,
46434                 width: 130,
46435                 listeners : {
46436                     'select': function(c, r, i) {
46437                         // initial support only for on class per el..
46438                         tb.selectedNode.className =  r ? r.get('val') : '';
46439                         editorcore.syncValue();
46440                     }
46441                 }
46442     
46443             }));
46444         }
46445         
46446         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46447         var tbops = tbc.options;
46448         
46449         for (var i in tlist) {
46450             
46451             var item = tlist[i];
46452             tb.add(item.title + ":&nbsp;");
46453             
46454             
46455             //optname == used so you can configure the options available..
46456             var opts = item.opts ? item.opts : false;
46457             if (item.optname) {
46458                 opts = tbops[item.optname];
46459            
46460             }
46461             
46462             if (opts) {
46463                 // opts == pulldown..
46464                 tb.addField( new Roo.form.ComboBox({
46465                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46466                         id : 'val',
46467                         fields: ['val', 'display'],
46468                         data : opts  
46469                     }),
46470                     name : '-roo-edit-' + i,
46471                     attrname : i,
46472                     stylename : item.style ? item.style : false,
46473                     displayField: item.displayField ? item.displayField : 'val',
46474                     valueField :  'val',
46475                     typeAhead: false,
46476                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46477                     editable : false,
46478                     triggerAction: 'all',
46479                     emptyText:'Select',
46480                     selectOnFocus:true,
46481                     width: item.width ? item.width  : 130,
46482                     listeners : {
46483                         'select': function(c, r, i) {
46484                             if (c.stylename) {
46485                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46486                                 return;
46487                             }
46488                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46489                         }
46490                     }
46491
46492                 }));
46493                 continue;
46494                     
46495                  
46496                 
46497                 tb.addField( new Roo.form.TextField({
46498                     name: i,
46499                     width: 100,
46500                     //allowBlank:false,
46501                     value: ''
46502                 }));
46503                 continue;
46504             }
46505             tb.addField( new Roo.form.TextField({
46506                 name: '-roo-edit-' + i,
46507                 attrname : i,
46508                 
46509                 width: item.width,
46510                 //allowBlank:true,
46511                 value: '',
46512                 listeners: {
46513                     'change' : function(f, nv, ov) {
46514                         tb.selectedNode.setAttribute(f.attrname, nv);
46515                         editorcore.syncValue();
46516                     }
46517                 }
46518             }));
46519              
46520         }
46521         
46522         var _this = this;
46523         
46524         if(nm == 'BODY'){
46525             tb.addSeparator();
46526         
46527             tb.addButton( {
46528                 text: 'Stylesheets',
46529
46530                 listeners : {
46531                     click : function ()
46532                     {
46533                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46534                     }
46535                 }
46536             });
46537         }
46538         
46539         tb.addFill();
46540         tb.addButton( {
46541             text: 'Remove Tag',
46542     
46543             listeners : {
46544                 click : function ()
46545                 {
46546                     // remove
46547                     // undo does not work.
46548                      
46549                     var sn = tb.selectedNode;
46550                     
46551                     var pn = sn.parentNode;
46552                     
46553                     var stn =  sn.childNodes[0];
46554                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46555                     while (sn.childNodes.length) {
46556                         var node = sn.childNodes[0];
46557                         sn.removeChild(node);
46558                         //Roo.log(node);
46559                         pn.insertBefore(node, sn);
46560                         
46561                     }
46562                     pn.removeChild(sn);
46563                     var range = editorcore.createRange();
46564         
46565                     range.setStart(stn,0);
46566                     range.setEnd(en,0); //????
46567                     //range.selectNode(sel);
46568                     
46569                     
46570                     var selection = editorcore.getSelection();
46571                     selection.removeAllRanges();
46572                     selection.addRange(range);
46573                     
46574                     
46575                     
46576                     //_this.updateToolbar(null, null, pn);
46577                     _this.updateToolbar(null, null, null);
46578                     _this.footDisp.dom.innerHTML = ''; 
46579                 }
46580             }
46581             
46582                     
46583                 
46584             
46585         });
46586         
46587         
46588         tb.el.on('click', function(e){
46589             e.preventDefault(); // what does this do?
46590         });
46591         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46592         tb.el.hide();
46593         tb.name = nm;
46594         // dont need to disable them... as they will get hidden
46595         return tb;
46596          
46597         
46598     },
46599     buildFooter : function()
46600     {
46601         
46602         var fel = this.editor.wrap.createChild();
46603         this.footer = new Roo.Toolbar(fel);
46604         // toolbar has scrolly on left / right?
46605         var footDisp= new Roo.Toolbar.Fill();
46606         var _t = this;
46607         this.footer.add(
46608             {
46609                 text : '&lt;',
46610                 xtype: 'Button',
46611                 handler : function() {
46612                     _t.footDisp.scrollTo('left',0,true)
46613                 }
46614             }
46615         );
46616         this.footer.add( footDisp );
46617         this.footer.add( 
46618             {
46619                 text : '&gt;',
46620                 xtype: 'Button',
46621                 handler : function() {
46622                     // no animation..
46623                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46624                 }
46625             }
46626         );
46627         var fel = Roo.get(footDisp.el);
46628         fel.addClass('x-editor-context');
46629         this.footDispWrap = fel; 
46630         this.footDispWrap.overflow  = 'hidden';
46631         
46632         this.footDisp = fel.createChild();
46633         this.footDispWrap.on('click', this.onContextClick, this)
46634         
46635         
46636     },
46637     onContextClick : function (ev,dom)
46638     {
46639         ev.preventDefault();
46640         var  cn = dom.className;
46641         //Roo.log(cn);
46642         if (!cn.match(/x-ed-loc-/)) {
46643             return;
46644         }
46645         var n = cn.split('-').pop();
46646         var ans = this.footerEls;
46647         var sel = ans[n];
46648         
46649          // pick
46650         var range = this.editorcore.createRange();
46651         
46652         range.selectNodeContents(sel);
46653         //range.selectNode(sel);
46654         
46655         
46656         var selection = this.editorcore.getSelection();
46657         selection.removeAllRanges();
46658         selection.addRange(range);
46659         
46660         
46661         
46662         this.updateToolbar(null, null, sel);
46663         
46664         
46665     }
46666     
46667     
46668     
46669     
46670     
46671 });
46672
46673
46674
46675
46676
46677 /*
46678  * Based on:
46679  * Ext JS Library 1.1.1
46680  * Copyright(c) 2006-2007, Ext JS, LLC.
46681  *
46682  * Originally Released Under LGPL - original licence link has changed is not relivant.
46683  *
46684  * Fork - LGPL
46685  * <script type="text/javascript">
46686  */
46687  
46688 /**
46689  * @class Roo.form.BasicForm
46690  * @extends Roo.util.Observable
46691  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46692  * @constructor
46693  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46694  * @param {Object} config Configuration options
46695  */
46696 Roo.form.BasicForm = function(el, config){
46697     this.allItems = [];
46698     this.childForms = [];
46699     Roo.apply(this, config);
46700     /*
46701      * The Roo.form.Field items in this form.
46702      * @type MixedCollection
46703      */
46704      
46705      
46706     this.items = new Roo.util.MixedCollection(false, function(o){
46707         return o.id || (o.id = Roo.id());
46708     });
46709     this.addEvents({
46710         /**
46711          * @event beforeaction
46712          * Fires before any action is performed. Return false to cancel the action.
46713          * @param {Form} this
46714          * @param {Action} action The action to be performed
46715          */
46716         beforeaction: true,
46717         /**
46718          * @event actionfailed
46719          * Fires when an action fails.
46720          * @param {Form} this
46721          * @param {Action} action The action that failed
46722          */
46723         actionfailed : true,
46724         /**
46725          * @event actioncomplete
46726          * Fires when an action is completed.
46727          * @param {Form} this
46728          * @param {Action} action The action that completed
46729          */
46730         actioncomplete : true
46731     });
46732     if(el){
46733         this.initEl(el);
46734     }
46735     Roo.form.BasicForm.superclass.constructor.call(this);
46736 };
46737
46738 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46739     /**
46740      * @cfg {String} method
46741      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46742      */
46743     /**
46744      * @cfg {DataReader} reader
46745      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46746      * This is optional as there is built-in support for processing JSON.
46747      */
46748     /**
46749      * @cfg {DataReader} errorReader
46750      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46751      * This is completely optional as there is built-in support for processing JSON.
46752      */
46753     /**
46754      * @cfg {String} url
46755      * The URL to use for form actions if one isn't supplied in the action options.
46756      */
46757     /**
46758      * @cfg {Boolean} fileUpload
46759      * Set to true if this form is a file upload.
46760      */
46761      
46762     /**
46763      * @cfg {Object} baseParams
46764      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46765      */
46766      /**
46767      
46768     /**
46769      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46770      */
46771     timeout: 30,
46772
46773     // private
46774     activeAction : null,
46775
46776     /**
46777      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46778      * or setValues() data instead of when the form was first created.
46779      */
46780     trackResetOnLoad : false,
46781     
46782     
46783     /**
46784      * childForms - used for multi-tab forms
46785      * @type {Array}
46786      */
46787     childForms : false,
46788     
46789     /**
46790      * allItems - full list of fields.
46791      * @type {Array}
46792      */
46793     allItems : false,
46794     
46795     /**
46796      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46797      * element by passing it or its id or mask the form itself by passing in true.
46798      * @type Mixed
46799      */
46800     waitMsgTarget : false,
46801
46802     // private
46803     initEl : function(el){
46804         this.el = Roo.get(el);
46805         this.id = this.el.id || Roo.id();
46806         this.el.on('submit', this.onSubmit, this);
46807         this.el.addClass('x-form');
46808     },
46809
46810     // private
46811     onSubmit : function(e){
46812         e.stopEvent();
46813     },
46814
46815     /**
46816      * Returns true if client-side validation on the form is successful.
46817      * @return Boolean
46818      */
46819     isValid : function(){
46820         var valid = true;
46821         this.items.each(function(f){
46822            if(!f.validate()){
46823                valid = false;
46824            }
46825         });
46826         return valid;
46827     },
46828
46829     /**
46830      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46831      * @return Boolean
46832      */
46833     isDirty : function(){
46834         var dirty = false;
46835         this.items.each(function(f){
46836            if(f.isDirty()){
46837                dirty = true;
46838                return false;
46839            }
46840         });
46841         return dirty;
46842     },
46843     
46844     /**
46845      * Returns true if any fields in this form have changed since their original load. (New version)
46846      * @return Boolean
46847      */
46848     
46849     hasChanged : function()
46850     {
46851         var dirty = false;
46852         this.items.each(function(f){
46853            if(f.hasChanged()){
46854                dirty = true;
46855                return false;
46856            }
46857         });
46858         return dirty;
46859         
46860     },
46861     /**
46862      * Resets all hasChanged to 'false' -
46863      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46864      * So hasChanged storage is only to be used for this purpose
46865      * @return Boolean
46866      */
46867     resetHasChanged : function()
46868     {
46869         this.items.each(function(f){
46870            f.resetHasChanged();
46871         });
46872         
46873     },
46874     
46875     
46876     /**
46877      * Performs a predefined action (submit or load) or custom actions you define on this form.
46878      * @param {String} actionName The name of the action type
46879      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46880      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46881      * accept other config options):
46882      * <pre>
46883 Property          Type             Description
46884 ----------------  ---------------  ----------------------------------------------------------------------------------
46885 url               String           The url for the action (defaults to the form's url)
46886 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46887 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46888 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46889                                    validate the form on the client (defaults to false)
46890      * </pre>
46891      * @return {BasicForm} this
46892      */
46893     doAction : function(action, options){
46894         if(typeof action == 'string'){
46895             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46896         }
46897         if(this.fireEvent('beforeaction', this, action) !== false){
46898             this.beforeAction(action);
46899             action.run.defer(100, action);
46900         }
46901         return this;
46902     },
46903
46904     /**
46905      * Shortcut to do a submit action.
46906      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46907      * @return {BasicForm} this
46908      */
46909     submit : function(options){
46910         this.doAction('submit', options);
46911         return this;
46912     },
46913
46914     /**
46915      * Shortcut to do a load action.
46916      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46917      * @return {BasicForm} this
46918      */
46919     load : function(options){
46920         this.doAction('load', options);
46921         return this;
46922     },
46923
46924     /**
46925      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46926      * @param {Record} record The record to edit
46927      * @return {BasicForm} this
46928      */
46929     updateRecord : function(record){
46930         record.beginEdit();
46931         var fs = record.fields;
46932         fs.each(function(f){
46933             var field = this.findField(f.name);
46934             if(field){
46935                 record.set(f.name, field.getValue());
46936             }
46937         }, this);
46938         record.endEdit();
46939         return this;
46940     },
46941
46942     /**
46943      * Loads an Roo.data.Record into this form.
46944      * @param {Record} record The record to load
46945      * @return {BasicForm} this
46946      */
46947     loadRecord : function(record){
46948         this.setValues(record.data);
46949         return this;
46950     },
46951
46952     // private
46953     beforeAction : function(action){
46954         var o = action.options;
46955         
46956        
46957         if(this.waitMsgTarget === true){
46958             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46959         }else if(this.waitMsgTarget){
46960             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46961             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46962         }else {
46963             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46964         }
46965          
46966     },
46967
46968     // private
46969     afterAction : function(action, success){
46970         this.activeAction = null;
46971         var o = action.options;
46972         
46973         if(this.waitMsgTarget === true){
46974             this.el.unmask();
46975         }else if(this.waitMsgTarget){
46976             this.waitMsgTarget.unmask();
46977         }else{
46978             Roo.MessageBox.updateProgress(1);
46979             Roo.MessageBox.hide();
46980         }
46981          
46982         if(success){
46983             if(o.reset){
46984                 this.reset();
46985             }
46986             Roo.callback(o.success, o.scope, [this, action]);
46987             this.fireEvent('actioncomplete', this, action);
46988             
46989         }else{
46990             
46991             // failure condition..
46992             // we have a scenario where updates need confirming.
46993             // eg. if a locking scenario exists..
46994             // we look for { errors : { needs_confirm : true }} in the response.
46995             if (
46996                 (typeof(action.result) != 'undefined')  &&
46997                 (typeof(action.result.errors) != 'undefined')  &&
46998                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46999            ){
47000                 var _t = this;
47001                 Roo.MessageBox.confirm(
47002                     "Change requires confirmation",
47003                     action.result.errorMsg,
47004                     function(r) {
47005                         if (r != 'yes') {
47006                             return;
47007                         }
47008                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47009                     }
47010                     
47011                 );
47012                 
47013                 
47014                 
47015                 return;
47016             }
47017             
47018             Roo.callback(o.failure, o.scope, [this, action]);
47019             // show an error message if no failed handler is set..
47020             if (!this.hasListener('actionfailed')) {
47021                 Roo.MessageBox.alert("Error",
47022                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47023                         action.result.errorMsg :
47024                         "Saving Failed, please check your entries or try again"
47025                 );
47026             }
47027             
47028             this.fireEvent('actionfailed', this, action);
47029         }
47030         
47031     },
47032
47033     /**
47034      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47035      * @param {String} id The value to search for
47036      * @return Field
47037      */
47038     findField : function(id){
47039         var field = this.items.get(id);
47040         if(!field){
47041             this.items.each(function(f){
47042                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47043                     field = f;
47044                     return false;
47045                 }
47046             });
47047         }
47048         return field || null;
47049     },
47050
47051     /**
47052      * Add a secondary form to this one, 
47053      * Used to provide tabbed forms. One form is primary, with hidden values 
47054      * which mirror the elements from the other forms.
47055      * 
47056      * @param {Roo.form.Form} form to add.
47057      * 
47058      */
47059     addForm : function(form)
47060     {
47061        
47062         if (this.childForms.indexOf(form) > -1) {
47063             // already added..
47064             return;
47065         }
47066         this.childForms.push(form);
47067         var n = '';
47068         Roo.each(form.allItems, function (fe) {
47069             
47070             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47071             if (this.findField(n)) { // already added..
47072                 return;
47073             }
47074             var add = new Roo.form.Hidden({
47075                 name : n
47076             });
47077             add.render(this.el);
47078             
47079             this.add( add );
47080         }, this);
47081         
47082     },
47083     /**
47084      * Mark fields in this form invalid in bulk.
47085      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47086      * @return {BasicForm} this
47087      */
47088     markInvalid : function(errors){
47089         if(errors instanceof Array){
47090             for(var i = 0, len = errors.length; i < len; i++){
47091                 var fieldError = errors[i];
47092                 var f = this.findField(fieldError.id);
47093                 if(f){
47094                     f.markInvalid(fieldError.msg);
47095                 }
47096             }
47097         }else{
47098             var field, id;
47099             for(id in errors){
47100                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47101                     field.markInvalid(errors[id]);
47102                 }
47103             }
47104         }
47105         Roo.each(this.childForms || [], function (f) {
47106             f.markInvalid(errors);
47107         });
47108         
47109         return this;
47110     },
47111
47112     /**
47113      * Set values for fields in this form in bulk.
47114      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47115      * @return {BasicForm} this
47116      */
47117     setValues : function(values){
47118         if(values instanceof Array){ // array of objects
47119             for(var i = 0, len = values.length; i < len; i++){
47120                 var v = values[i];
47121                 var f = this.findField(v.id);
47122                 if(f){
47123                     f.setValue(v.value);
47124                     if(this.trackResetOnLoad){
47125                         f.originalValue = f.getValue();
47126                     }
47127                 }
47128             }
47129         }else{ // object hash
47130             var field, id;
47131             for(id in values){
47132                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47133                     
47134                     if (field.setFromData && 
47135                         field.valueField && 
47136                         field.displayField &&
47137                         // combos' with local stores can 
47138                         // be queried via setValue()
47139                         // to set their value..
47140                         (field.store && !field.store.isLocal)
47141                         ) {
47142                         // it's a combo
47143                         var sd = { };
47144                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47145                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47146                         field.setFromData(sd);
47147                         
47148                     } else {
47149                         field.setValue(values[id]);
47150                     }
47151                     
47152                     
47153                     if(this.trackResetOnLoad){
47154                         field.originalValue = field.getValue();
47155                     }
47156                 }
47157             }
47158         }
47159         this.resetHasChanged();
47160         
47161         
47162         Roo.each(this.childForms || [], function (f) {
47163             f.setValues(values);
47164             f.resetHasChanged();
47165         });
47166                 
47167         return this;
47168     },
47169
47170     /**
47171      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47172      * they are returned as an array.
47173      * @param {Boolean} asString
47174      * @return {Object}
47175      */
47176     getValues : function(asString){
47177         if (this.childForms) {
47178             // copy values from the child forms
47179             Roo.each(this.childForms, function (f) {
47180                 this.setValues(f.getValues());
47181             }, this);
47182         }
47183         
47184         
47185         
47186         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47187         if(asString === true){
47188             return fs;
47189         }
47190         return Roo.urlDecode(fs);
47191     },
47192     
47193     /**
47194      * Returns the fields in this form as an object with key/value pairs. 
47195      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47196      * @return {Object}
47197      */
47198     getFieldValues : function(with_hidden)
47199     {
47200         if (this.childForms) {
47201             // copy values from the child forms
47202             // should this call getFieldValues - probably not as we do not currently copy
47203             // hidden fields when we generate..
47204             Roo.each(this.childForms, function (f) {
47205                 this.setValues(f.getValues());
47206             }, this);
47207         }
47208         
47209         var ret = {};
47210         this.items.each(function(f){
47211             if (!f.getName()) {
47212                 return;
47213             }
47214             var v = f.getValue();
47215             if (f.inputType =='radio') {
47216                 if (typeof(ret[f.getName()]) == 'undefined') {
47217                     ret[f.getName()] = ''; // empty..
47218                 }
47219                 
47220                 if (!f.el.dom.checked) {
47221                     return;
47222                     
47223                 }
47224                 v = f.el.dom.value;
47225                 
47226             }
47227             
47228             // not sure if this supported any more..
47229             if ((typeof(v) == 'object') && f.getRawValue) {
47230                 v = f.getRawValue() ; // dates..
47231             }
47232             // combo boxes where name != hiddenName...
47233             if (f.name != f.getName()) {
47234                 ret[f.name] = f.getRawValue();
47235             }
47236             ret[f.getName()] = v;
47237         });
47238         
47239         return ret;
47240     },
47241
47242     /**
47243      * Clears all invalid messages in this form.
47244      * @return {BasicForm} this
47245      */
47246     clearInvalid : function(){
47247         this.items.each(function(f){
47248            f.clearInvalid();
47249         });
47250         
47251         Roo.each(this.childForms || [], function (f) {
47252             f.clearInvalid();
47253         });
47254         
47255         
47256         return this;
47257     },
47258
47259     /**
47260      * Resets this form.
47261      * @return {BasicForm} this
47262      */
47263     reset : function(){
47264         this.items.each(function(f){
47265             f.reset();
47266         });
47267         
47268         Roo.each(this.childForms || [], function (f) {
47269             f.reset();
47270         });
47271         this.resetHasChanged();
47272         
47273         return this;
47274     },
47275
47276     /**
47277      * Add Roo.form components to this form.
47278      * @param {Field} field1
47279      * @param {Field} field2 (optional)
47280      * @param {Field} etc (optional)
47281      * @return {BasicForm} this
47282      */
47283     add : function(){
47284         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47285         return this;
47286     },
47287
47288
47289     /**
47290      * Removes a field from the items collection (does NOT remove its markup).
47291      * @param {Field} field
47292      * @return {BasicForm} this
47293      */
47294     remove : function(field){
47295         this.items.remove(field);
47296         return this;
47297     },
47298
47299     /**
47300      * Looks at the fields in this form, checks them for an id attribute,
47301      * and calls applyTo on the existing dom element with that id.
47302      * @return {BasicForm} this
47303      */
47304     render : function(){
47305         this.items.each(function(f){
47306             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47307                 f.applyTo(f.id);
47308             }
47309         });
47310         return this;
47311     },
47312
47313     /**
47314      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47315      * @param {Object} values
47316      * @return {BasicForm} this
47317      */
47318     applyToFields : function(o){
47319         this.items.each(function(f){
47320            Roo.apply(f, o);
47321         });
47322         return this;
47323     },
47324
47325     /**
47326      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47327      * @param {Object} values
47328      * @return {BasicForm} this
47329      */
47330     applyIfToFields : function(o){
47331         this.items.each(function(f){
47332            Roo.applyIf(f, o);
47333         });
47334         return this;
47335     }
47336 });
47337
47338 // back compat
47339 Roo.BasicForm = Roo.form.BasicForm;/*
47340  * Based on:
47341  * Ext JS Library 1.1.1
47342  * Copyright(c) 2006-2007, Ext JS, LLC.
47343  *
47344  * Originally Released Under LGPL - original licence link has changed is not relivant.
47345  *
47346  * Fork - LGPL
47347  * <script type="text/javascript">
47348  */
47349
47350 /**
47351  * @class Roo.form.Form
47352  * @extends Roo.form.BasicForm
47353  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47354  * @constructor
47355  * @param {Object} config Configuration options
47356  */
47357 Roo.form.Form = function(config){
47358     var xitems =  [];
47359     if (config.items) {
47360         xitems = config.items;
47361         delete config.items;
47362     }
47363    
47364     
47365     Roo.form.Form.superclass.constructor.call(this, null, config);
47366     this.url = this.url || this.action;
47367     if(!this.root){
47368         this.root = new Roo.form.Layout(Roo.applyIf({
47369             id: Roo.id()
47370         }, config));
47371     }
47372     this.active = this.root;
47373     /**
47374      * Array of all the buttons that have been added to this form via {@link addButton}
47375      * @type Array
47376      */
47377     this.buttons = [];
47378     this.allItems = [];
47379     this.addEvents({
47380         /**
47381          * @event clientvalidation
47382          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47383          * @param {Form} this
47384          * @param {Boolean} valid true if the form has passed client-side validation
47385          */
47386         clientvalidation: true,
47387         /**
47388          * @event rendered
47389          * Fires when the form is rendered
47390          * @param {Roo.form.Form} form
47391          */
47392         rendered : true
47393     });
47394     
47395     if (this.progressUrl) {
47396             // push a hidden field onto the list of fields..
47397             this.addxtype( {
47398                     xns: Roo.form, 
47399                     xtype : 'Hidden', 
47400                     name : 'UPLOAD_IDENTIFIER' 
47401             });
47402         }
47403         
47404     
47405     Roo.each(xitems, this.addxtype, this);
47406     
47407     
47408     
47409 };
47410
47411 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47412     /**
47413      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47414      */
47415     /**
47416      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47417      */
47418     /**
47419      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47420      */
47421     buttonAlign:'center',
47422
47423     /**
47424      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47425      */
47426     minButtonWidth:75,
47427
47428     /**
47429      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47430      * This property cascades to child containers if not set.
47431      */
47432     labelAlign:'left',
47433
47434     /**
47435      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47436      * fires a looping event with that state. This is required to bind buttons to the valid
47437      * state using the config value formBind:true on the button.
47438      */
47439     monitorValid : false,
47440
47441     /**
47442      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47443      */
47444     monitorPoll : 200,
47445     
47446     /**
47447      * @cfg {String} progressUrl - Url to return progress data 
47448      */
47449     
47450     progressUrl : false,
47451     /**
47452      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
47453      * sending a formdata with extra parameters - eg uploaded elements.
47454      */
47455     
47456     formData : false,
47457     
47458     /**
47459      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47460      * fields are added and the column is closed. If no fields are passed the column remains open
47461      * until end() is called.
47462      * @param {Object} config The config to pass to the column
47463      * @param {Field} field1 (optional)
47464      * @param {Field} field2 (optional)
47465      * @param {Field} etc (optional)
47466      * @return Column The column container object
47467      */
47468     column : function(c){
47469         var col = new Roo.form.Column(c);
47470         this.start(col);
47471         if(arguments.length > 1){ // duplicate code required because of Opera
47472             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47473             this.end();
47474         }
47475         return col;
47476     },
47477
47478     /**
47479      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47480      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47481      * until end() is called.
47482      * @param {Object} config The config to pass to the fieldset
47483      * @param {Field} field1 (optional)
47484      * @param {Field} field2 (optional)
47485      * @param {Field} etc (optional)
47486      * @return FieldSet The fieldset container object
47487      */
47488     fieldset : function(c){
47489         var fs = new Roo.form.FieldSet(c);
47490         this.start(fs);
47491         if(arguments.length > 1){ // duplicate code required because of Opera
47492             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47493             this.end();
47494         }
47495         return fs;
47496     },
47497
47498     /**
47499      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47500      * fields are added and the container is closed. If no fields are passed the container remains open
47501      * until end() is called.
47502      * @param {Object} config The config to pass to the Layout
47503      * @param {Field} field1 (optional)
47504      * @param {Field} field2 (optional)
47505      * @param {Field} etc (optional)
47506      * @return Layout The container object
47507      */
47508     container : function(c){
47509         var l = new Roo.form.Layout(c);
47510         this.start(l);
47511         if(arguments.length > 1){ // duplicate code required because of Opera
47512             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47513             this.end();
47514         }
47515         return l;
47516     },
47517
47518     /**
47519      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47520      * @param {Object} container A Roo.form.Layout or subclass of Layout
47521      * @return {Form} this
47522      */
47523     start : function(c){
47524         // cascade label info
47525         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47526         this.active.stack.push(c);
47527         c.ownerCt = this.active;
47528         this.active = c;
47529         return this;
47530     },
47531
47532     /**
47533      * Closes the current open container
47534      * @return {Form} this
47535      */
47536     end : function(){
47537         if(this.active == this.root){
47538             return this;
47539         }
47540         this.active = this.active.ownerCt;
47541         return this;
47542     },
47543
47544     /**
47545      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47546      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47547      * as the label of the field.
47548      * @param {Field} field1
47549      * @param {Field} field2 (optional)
47550      * @param {Field} etc. (optional)
47551      * @return {Form} this
47552      */
47553     add : function(){
47554         this.active.stack.push.apply(this.active.stack, arguments);
47555         this.allItems.push.apply(this.allItems,arguments);
47556         var r = [];
47557         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47558             if(a[i].isFormField){
47559                 r.push(a[i]);
47560             }
47561         }
47562         if(r.length > 0){
47563             Roo.form.Form.superclass.add.apply(this, r);
47564         }
47565         return this;
47566     },
47567     
47568
47569     
47570     
47571     
47572      /**
47573      * Find any element that has been added to a form, using it's ID or name
47574      * This can include framesets, columns etc. along with regular fields..
47575      * @param {String} id - id or name to find.
47576      
47577      * @return {Element} e - or false if nothing found.
47578      */
47579     findbyId : function(id)
47580     {
47581         var ret = false;
47582         if (!id) {
47583             return ret;
47584         }
47585         Roo.each(this.allItems, function(f){
47586             if (f.id == id || f.name == id ){
47587                 ret = f;
47588                 return false;
47589             }
47590         });
47591         return ret;
47592     },
47593
47594     
47595     
47596     /**
47597      * Render this form into the passed container. This should only be called once!
47598      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47599      * @return {Form} this
47600      */
47601     render : function(ct)
47602     {
47603         
47604         
47605         
47606         ct = Roo.get(ct);
47607         var o = this.autoCreate || {
47608             tag: 'form',
47609             method : this.method || 'POST',
47610             id : this.id || Roo.id()
47611         };
47612         this.initEl(ct.createChild(o));
47613
47614         this.root.render(this.el);
47615         
47616        
47617              
47618         this.items.each(function(f){
47619             f.render('x-form-el-'+f.id);
47620         });
47621
47622         if(this.buttons.length > 0){
47623             // tables are required to maintain order and for correct IE layout
47624             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47625                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47626                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47627             }}, null, true);
47628             var tr = tb.getElementsByTagName('tr')[0];
47629             for(var i = 0, len = this.buttons.length; i < len; i++) {
47630                 var b = this.buttons[i];
47631                 var td = document.createElement('td');
47632                 td.className = 'x-form-btn-td';
47633                 b.render(tr.appendChild(td));
47634             }
47635         }
47636         if(this.monitorValid){ // initialize after render
47637             this.startMonitoring();
47638         }
47639         this.fireEvent('rendered', this);
47640         return this;
47641     },
47642
47643     /**
47644      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47645      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47646      * object or a valid Roo.DomHelper element config
47647      * @param {Function} handler The function called when the button is clicked
47648      * @param {Object} scope (optional) The scope of the handler function
47649      * @return {Roo.Button}
47650      */
47651     addButton : function(config, handler, scope){
47652         var bc = {
47653             handler: handler,
47654             scope: scope,
47655             minWidth: this.minButtonWidth,
47656             hideParent:true
47657         };
47658         if(typeof config == "string"){
47659             bc.text = config;
47660         }else{
47661             Roo.apply(bc, config);
47662         }
47663         var btn = new Roo.Button(null, bc);
47664         this.buttons.push(btn);
47665         return btn;
47666     },
47667
47668      /**
47669      * Adds a series of form elements (using the xtype property as the factory method.
47670      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47671      * @param {Object} config 
47672      */
47673     
47674     addxtype : function()
47675     {
47676         var ar = Array.prototype.slice.call(arguments, 0);
47677         var ret = false;
47678         for(var i = 0; i < ar.length; i++) {
47679             if (!ar[i]) {
47680                 continue; // skip -- if this happends something invalid got sent, we 
47681                 // should ignore it, as basically that interface element will not show up
47682                 // and that should be pretty obvious!!
47683             }
47684             
47685             if (Roo.form[ar[i].xtype]) {
47686                 ar[i].form = this;
47687                 var fe = Roo.factory(ar[i], Roo.form);
47688                 if (!ret) {
47689                     ret = fe;
47690                 }
47691                 fe.form = this;
47692                 if (fe.store) {
47693                     fe.store.form = this;
47694                 }
47695                 if (fe.isLayout) {  
47696                          
47697                     this.start(fe);
47698                     this.allItems.push(fe);
47699                     if (fe.items && fe.addxtype) {
47700                         fe.addxtype.apply(fe, fe.items);
47701                         delete fe.items;
47702                     }
47703                      this.end();
47704                     continue;
47705                 }
47706                 
47707                 
47708                  
47709                 this.add(fe);
47710               //  console.log('adding ' + ar[i].xtype);
47711             }
47712             if (ar[i].xtype == 'Button') {  
47713                 //console.log('adding button');
47714                 //console.log(ar[i]);
47715                 this.addButton(ar[i]);
47716                 this.allItems.push(fe);
47717                 continue;
47718             }
47719             
47720             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47721                 alert('end is not supported on xtype any more, use items');
47722             //    this.end();
47723             //    //console.log('adding end');
47724             }
47725             
47726         }
47727         return ret;
47728     },
47729     
47730     /**
47731      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47732      * option "monitorValid"
47733      */
47734     startMonitoring : function(){
47735         if(!this.bound){
47736             this.bound = true;
47737             Roo.TaskMgr.start({
47738                 run : this.bindHandler,
47739                 interval : this.monitorPoll || 200,
47740                 scope: this
47741             });
47742         }
47743     },
47744
47745     /**
47746      * Stops monitoring of the valid state of this form
47747      */
47748     stopMonitoring : function(){
47749         this.bound = false;
47750     },
47751
47752     // private
47753     bindHandler : function(){
47754         if(!this.bound){
47755             return false; // stops binding
47756         }
47757         var valid = true;
47758         this.items.each(function(f){
47759             if(!f.isValid(true)){
47760                 valid = false;
47761                 return false;
47762             }
47763         });
47764         for(var i = 0, len = this.buttons.length; i < len; i++){
47765             var btn = this.buttons[i];
47766             if(btn.formBind === true && btn.disabled === valid){
47767                 btn.setDisabled(!valid);
47768             }
47769         }
47770         this.fireEvent('clientvalidation', this, valid);
47771     }
47772     
47773     
47774     
47775     
47776     
47777     
47778     
47779     
47780 });
47781
47782
47783 // back compat
47784 Roo.Form = Roo.form.Form;
47785 /*
47786  * Based on:
47787  * Ext JS Library 1.1.1
47788  * Copyright(c) 2006-2007, Ext JS, LLC.
47789  *
47790  * Originally Released Under LGPL - original licence link has changed is not relivant.
47791  *
47792  * Fork - LGPL
47793  * <script type="text/javascript">
47794  */
47795
47796 // as we use this in bootstrap.
47797 Roo.namespace('Roo.form');
47798  /**
47799  * @class Roo.form.Action
47800  * Internal Class used to handle form actions
47801  * @constructor
47802  * @param {Roo.form.BasicForm} el The form element or its id
47803  * @param {Object} config Configuration options
47804  */
47805
47806  
47807  
47808 // define the action interface
47809 Roo.form.Action = function(form, options){
47810     this.form = form;
47811     this.options = options || {};
47812 };
47813 /**
47814  * Client Validation Failed
47815  * @const 
47816  */
47817 Roo.form.Action.CLIENT_INVALID = 'client';
47818 /**
47819  * Server Validation Failed
47820  * @const 
47821  */
47822 Roo.form.Action.SERVER_INVALID = 'server';
47823  /**
47824  * Connect to Server Failed
47825  * @const 
47826  */
47827 Roo.form.Action.CONNECT_FAILURE = 'connect';
47828 /**
47829  * Reading Data from Server Failed
47830  * @const 
47831  */
47832 Roo.form.Action.LOAD_FAILURE = 'load';
47833
47834 Roo.form.Action.prototype = {
47835     type : 'default',
47836     failureType : undefined,
47837     response : undefined,
47838     result : undefined,
47839
47840     // interface method
47841     run : function(options){
47842
47843     },
47844
47845     // interface method
47846     success : function(response){
47847
47848     },
47849
47850     // interface method
47851     handleResponse : function(response){
47852
47853     },
47854
47855     // default connection failure
47856     failure : function(response){
47857         
47858         this.response = response;
47859         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47860         this.form.afterAction(this, false);
47861     },
47862
47863     processResponse : function(response){
47864         this.response = response;
47865         if(!response.responseText){
47866             return true;
47867         }
47868         this.result = this.handleResponse(response);
47869         return this.result;
47870     },
47871
47872     // utility functions used internally
47873     getUrl : function(appendParams){
47874         var url = this.options.url || this.form.url || this.form.el.dom.action;
47875         if(appendParams){
47876             var p = this.getParams();
47877             if(p){
47878                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47879             }
47880         }
47881         return url;
47882     },
47883
47884     getMethod : function(){
47885         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47886     },
47887
47888     getParams : function(){
47889         var bp = this.form.baseParams;
47890         var p = this.options.params;
47891         if(p){
47892             if(typeof p == "object"){
47893                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47894             }else if(typeof p == 'string' && bp){
47895                 p += '&' + Roo.urlEncode(bp);
47896             }
47897         }else if(bp){
47898             p = Roo.urlEncode(bp);
47899         }
47900         return p;
47901     },
47902
47903     createCallback : function(){
47904         return {
47905             success: this.success,
47906             failure: this.failure,
47907             scope: this,
47908             timeout: (this.form.timeout*1000),
47909             upload: this.form.fileUpload ? this.success : undefined
47910         };
47911     }
47912 };
47913
47914 Roo.form.Action.Submit = function(form, options){
47915     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47916 };
47917
47918 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47919     type : 'submit',
47920
47921     haveProgress : false,
47922     uploadComplete : false,
47923     
47924     // uploadProgress indicator.
47925     uploadProgress : function()
47926     {
47927         if (!this.form.progressUrl) {
47928             return;
47929         }
47930         
47931         if (!this.haveProgress) {
47932             Roo.MessageBox.progress("Uploading", "Uploading");
47933         }
47934         if (this.uploadComplete) {
47935            Roo.MessageBox.hide();
47936            return;
47937         }
47938         
47939         this.haveProgress = true;
47940    
47941         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47942         
47943         var c = new Roo.data.Connection();
47944         c.request({
47945             url : this.form.progressUrl,
47946             params: {
47947                 id : uid
47948             },
47949             method: 'GET',
47950             success : function(req){
47951                //console.log(data);
47952                 var rdata = false;
47953                 var edata;
47954                 try  {
47955                    rdata = Roo.decode(req.responseText)
47956                 } catch (e) {
47957                     Roo.log("Invalid data from server..");
47958                     Roo.log(edata);
47959                     return;
47960                 }
47961                 if (!rdata || !rdata.success) {
47962                     Roo.log(rdata);
47963                     Roo.MessageBox.alert(Roo.encode(rdata));
47964                     return;
47965                 }
47966                 var data = rdata.data;
47967                 
47968                 if (this.uploadComplete) {
47969                    Roo.MessageBox.hide();
47970                    return;
47971                 }
47972                    
47973                 if (data){
47974                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47975                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47976                     );
47977                 }
47978                 this.uploadProgress.defer(2000,this);
47979             },
47980        
47981             failure: function(data) {
47982                 Roo.log('progress url failed ');
47983                 Roo.log(data);
47984             },
47985             scope : this
47986         });
47987            
47988     },
47989     
47990     
47991     run : function()
47992     {
47993         // run get Values on the form, so it syncs any secondary forms.
47994         this.form.getValues();
47995         
47996         var o = this.options;
47997         var method = this.getMethod();
47998         var isPost = method == 'POST';
47999         if(o.clientValidation === false || this.form.isValid()){
48000             
48001             if (this.form.progressUrl) {
48002                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48003                     (new Date() * 1) + '' + Math.random());
48004                     
48005             } 
48006             
48007             
48008             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48009                 form:this.form.el.dom,
48010                 url:this.getUrl(!isPost),
48011                 method: method,
48012                 params:isPost ? this.getParams() : null,
48013                 isUpload: this.form.fileUpload,
48014                 formData : this.form.formData
48015             }));
48016             
48017             this.uploadProgress();
48018
48019         }else if (o.clientValidation !== false){ // client validation failed
48020             this.failureType = Roo.form.Action.CLIENT_INVALID;
48021             this.form.afterAction(this, false);
48022         }
48023     },
48024
48025     success : function(response)
48026     {
48027         this.uploadComplete= true;
48028         if (this.haveProgress) {
48029             Roo.MessageBox.hide();
48030         }
48031         
48032         
48033         var result = this.processResponse(response);
48034         if(result === true || result.success){
48035             this.form.afterAction(this, true);
48036             return;
48037         }
48038         if(result.errors){
48039             this.form.markInvalid(result.errors);
48040             this.failureType = Roo.form.Action.SERVER_INVALID;
48041         }
48042         this.form.afterAction(this, false);
48043     },
48044     failure : function(response)
48045     {
48046         this.uploadComplete= true;
48047         if (this.haveProgress) {
48048             Roo.MessageBox.hide();
48049         }
48050         
48051         this.response = response;
48052         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48053         this.form.afterAction(this, false);
48054     },
48055     
48056     handleResponse : function(response){
48057         if(this.form.errorReader){
48058             var rs = this.form.errorReader.read(response);
48059             var errors = [];
48060             if(rs.records){
48061                 for(var i = 0, len = rs.records.length; i < len; i++) {
48062                     var r = rs.records[i];
48063                     errors[i] = r.data;
48064                 }
48065             }
48066             if(errors.length < 1){
48067                 errors = null;
48068             }
48069             return {
48070                 success : rs.success,
48071                 errors : errors
48072             };
48073         }
48074         var ret = false;
48075         try {
48076             ret = Roo.decode(response.responseText);
48077         } catch (e) {
48078             ret = {
48079                 success: false,
48080                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48081                 errors : []
48082             };
48083         }
48084         return ret;
48085         
48086     }
48087 });
48088
48089
48090 Roo.form.Action.Load = function(form, options){
48091     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48092     this.reader = this.form.reader;
48093 };
48094
48095 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48096     type : 'load',
48097
48098     run : function(){
48099         
48100         Roo.Ajax.request(Roo.apply(
48101                 this.createCallback(), {
48102                     method:this.getMethod(),
48103                     url:this.getUrl(false),
48104                     params:this.getParams()
48105         }));
48106     },
48107
48108     success : function(response){
48109         
48110         var result = this.processResponse(response);
48111         if(result === true || !result.success || !result.data){
48112             this.failureType = Roo.form.Action.LOAD_FAILURE;
48113             this.form.afterAction(this, false);
48114             return;
48115         }
48116         this.form.clearInvalid();
48117         this.form.setValues(result.data);
48118         this.form.afterAction(this, true);
48119     },
48120
48121     handleResponse : function(response){
48122         if(this.form.reader){
48123             var rs = this.form.reader.read(response);
48124             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48125             return {
48126                 success : rs.success,
48127                 data : data
48128             };
48129         }
48130         return Roo.decode(response.responseText);
48131     }
48132 });
48133
48134 Roo.form.Action.ACTION_TYPES = {
48135     'load' : Roo.form.Action.Load,
48136     'submit' : Roo.form.Action.Submit
48137 };/*
48138  * Based on:
48139  * Ext JS Library 1.1.1
48140  * Copyright(c) 2006-2007, Ext JS, LLC.
48141  *
48142  * Originally Released Under LGPL - original licence link has changed is not relivant.
48143  *
48144  * Fork - LGPL
48145  * <script type="text/javascript">
48146  */
48147  
48148 /**
48149  * @class Roo.form.Layout
48150  * @extends Roo.Component
48151  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48152  * @constructor
48153  * @param {Object} config Configuration options
48154  */
48155 Roo.form.Layout = function(config){
48156     var xitems = [];
48157     if (config.items) {
48158         xitems = config.items;
48159         delete config.items;
48160     }
48161     Roo.form.Layout.superclass.constructor.call(this, config);
48162     this.stack = [];
48163     Roo.each(xitems, this.addxtype, this);
48164      
48165 };
48166
48167 Roo.extend(Roo.form.Layout, Roo.Component, {
48168     /**
48169      * @cfg {String/Object} autoCreate
48170      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48171      */
48172     /**
48173      * @cfg {String/Object/Function} style
48174      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48175      * a function which returns such a specification.
48176      */
48177     /**
48178      * @cfg {String} labelAlign
48179      * Valid values are "left," "top" and "right" (defaults to "left")
48180      */
48181     /**
48182      * @cfg {Number} labelWidth
48183      * Fixed width in pixels of all field labels (defaults to undefined)
48184      */
48185     /**
48186      * @cfg {Boolean} clear
48187      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48188      */
48189     clear : true,
48190     /**
48191      * @cfg {String} labelSeparator
48192      * The separator to use after field labels (defaults to ':')
48193      */
48194     labelSeparator : ':',
48195     /**
48196      * @cfg {Boolean} hideLabels
48197      * True to suppress the display of field labels in this layout (defaults to false)
48198      */
48199     hideLabels : false,
48200
48201     // private
48202     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48203     
48204     isLayout : true,
48205     
48206     // private
48207     onRender : function(ct, position){
48208         if(this.el){ // from markup
48209             this.el = Roo.get(this.el);
48210         }else {  // generate
48211             var cfg = this.getAutoCreate();
48212             this.el = ct.createChild(cfg, position);
48213         }
48214         if(this.style){
48215             this.el.applyStyles(this.style);
48216         }
48217         if(this.labelAlign){
48218             this.el.addClass('x-form-label-'+this.labelAlign);
48219         }
48220         if(this.hideLabels){
48221             this.labelStyle = "display:none";
48222             this.elementStyle = "padding-left:0;";
48223         }else{
48224             if(typeof this.labelWidth == 'number'){
48225                 this.labelStyle = "width:"+this.labelWidth+"px;";
48226                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48227             }
48228             if(this.labelAlign == 'top'){
48229                 this.labelStyle = "width:auto;";
48230                 this.elementStyle = "padding-left:0;";
48231             }
48232         }
48233         var stack = this.stack;
48234         var slen = stack.length;
48235         if(slen > 0){
48236             if(!this.fieldTpl){
48237                 var t = new Roo.Template(
48238                     '<div class="x-form-item {5}">',
48239                         '<label for="{0}" style="{2}">{1}{4}</label>',
48240                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48241                         '</div>',
48242                     '</div><div class="x-form-clear-left"></div>'
48243                 );
48244                 t.disableFormats = true;
48245                 t.compile();
48246                 Roo.form.Layout.prototype.fieldTpl = t;
48247             }
48248             for(var i = 0; i < slen; i++) {
48249                 if(stack[i].isFormField){
48250                     this.renderField(stack[i]);
48251                 }else{
48252                     this.renderComponent(stack[i]);
48253                 }
48254             }
48255         }
48256         if(this.clear){
48257             this.el.createChild({cls:'x-form-clear'});
48258         }
48259     },
48260
48261     // private
48262     renderField : function(f){
48263         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48264                f.id, //0
48265                f.fieldLabel, //1
48266                f.labelStyle||this.labelStyle||'', //2
48267                this.elementStyle||'', //3
48268                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48269                f.itemCls||this.itemCls||''  //5
48270        ], true).getPrevSibling());
48271     },
48272
48273     // private
48274     renderComponent : function(c){
48275         c.render(c.isLayout ? this.el : this.el.createChild());    
48276     },
48277     /**
48278      * Adds a object form elements (using the xtype property as the factory method.)
48279      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48280      * @param {Object} config 
48281      */
48282     addxtype : function(o)
48283     {
48284         // create the lement.
48285         o.form = this.form;
48286         var fe = Roo.factory(o, Roo.form);
48287         this.form.allItems.push(fe);
48288         this.stack.push(fe);
48289         
48290         if (fe.isFormField) {
48291             this.form.items.add(fe);
48292         }
48293          
48294         return fe;
48295     }
48296 });
48297
48298 /**
48299  * @class Roo.form.Column
48300  * @extends Roo.form.Layout
48301  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48302  * @constructor
48303  * @param {Object} config Configuration options
48304  */
48305 Roo.form.Column = function(config){
48306     Roo.form.Column.superclass.constructor.call(this, config);
48307 };
48308
48309 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48310     /**
48311      * @cfg {Number/String} width
48312      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48313      */
48314     /**
48315      * @cfg {String/Object} autoCreate
48316      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48317      */
48318
48319     // private
48320     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48321
48322     // private
48323     onRender : function(ct, position){
48324         Roo.form.Column.superclass.onRender.call(this, ct, position);
48325         if(this.width){
48326             this.el.setWidth(this.width);
48327         }
48328     }
48329 });
48330
48331
48332 /**
48333  * @class Roo.form.Row
48334  * @extends Roo.form.Layout
48335  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48336  * @constructor
48337  * @param {Object} config Configuration options
48338  */
48339
48340  
48341 Roo.form.Row = function(config){
48342     Roo.form.Row.superclass.constructor.call(this, config);
48343 };
48344  
48345 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48346       /**
48347      * @cfg {Number/String} width
48348      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48349      */
48350     /**
48351      * @cfg {Number/String} height
48352      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48353      */
48354     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48355     
48356     padWidth : 20,
48357     // private
48358     onRender : function(ct, position){
48359         //console.log('row render');
48360         if(!this.rowTpl){
48361             var t = new Roo.Template(
48362                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48363                     '<label for="{0}" style="{2}">{1}{4}</label>',
48364                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48365                     '</div>',
48366                 '</div>'
48367             );
48368             t.disableFormats = true;
48369             t.compile();
48370             Roo.form.Layout.prototype.rowTpl = t;
48371         }
48372         this.fieldTpl = this.rowTpl;
48373         
48374         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48375         var labelWidth = 100;
48376         
48377         if ((this.labelAlign != 'top')) {
48378             if (typeof this.labelWidth == 'number') {
48379                 labelWidth = this.labelWidth
48380             }
48381             this.padWidth =  20 + labelWidth;
48382             
48383         }
48384         
48385         Roo.form.Column.superclass.onRender.call(this, ct, position);
48386         if(this.width){
48387             this.el.setWidth(this.width);
48388         }
48389         if(this.height){
48390             this.el.setHeight(this.height);
48391         }
48392     },
48393     
48394     // private
48395     renderField : function(f){
48396         f.fieldEl = this.fieldTpl.append(this.el, [
48397                f.id, f.fieldLabel,
48398                f.labelStyle||this.labelStyle||'',
48399                this.elementStyle||'',
48400                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48401                f.itemCls||this.itemCls||'',
48402                f.width ? f.width + this.padWidth : 160 + this.padWidth
48403        ],true);
48404     }
48405 });
48406  
48407
48408 /**
48409  * @class Roo.form.FieldSet
48410  * @extends Roo.form.Layout
48411  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48412  * @constructor
48413  * @param {Object} config Configuration options
48414  */
48415 Roo.form.FieldSet = function(config){
48416     Roo.form.FieldSet.superclass.constructor.call(this, config);
48417 };
48418
48419 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48420     /**
48421      * @cfg {String} legend
48422      * The text to display as the legend for the FieldSet (defaults to '')
48423      */
48424     /**
48425      * @cfg {String/Object} autoCreate
48426      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48427      */
48428
48429     // private
48430     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48431
48432     // private
48433     onRender : function(ct, position){
48434         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48435         if(this.legend){
48436             this.setLegend(this.legend);
48437         }
48438     },
48439
48440     // private
48441     setLegend : function(text){
48442         if(this.rendered){
48443             this.el.child('legend').update(text);
48444         }
48445     }
48446 });/*
48447  * Based on:
48448  * Ext JS Library 1.1.1
48449  * Copyright(c) 2006-2007, Ext JS, LLC.
48450  *
48451  * Originally Released Under LGPL - original licence link has changed is not relivant.
48452  *
48453  * Fork - LGPL
48454  * <script type="text/javascript">
48455  */
48456 /**
48457  * @class Roo.form.VTypes
48458  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48459  * @singleton
48460  */
48461 Roo.form.VTypes = function(){
48462     // closure these in so they are only created once.
48463     var alpha = /^[a-zA-Z_]+$/;
48464     var alphanum = /^[a-zA-Z0-9_]+$/;
48465     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48466     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48467
48468     // All these messages and functions are configurable
48469     return {
48470         /**
48471          * The function used to validate email addresses
48472          * @param {String} value The email address
48473          */
48474         'email' : function(v){
48475             return email.test(v);
48476         },
48477         /**
48478          * The error text to display when the email validation function returns false
48479          * @type String
48480          */
48481         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48482         /**
48483          * The keystroke filter mask to be applied on email input
48484          * @type RegExp
48485          */
48486         'emailMask' : /[a-z0-9_\.\-@]/i,
48487
48488         /**
48489          * The function used to validate URLs
48490          * @param {String} value The URL
48491          */
48492         'url' : function(v){
48493             return url.test(v);
48494         },
48495         /**
48496          * The error text to display when the url validation function returns false
48497          * @type String
48498          */
48499         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48500         
48501         /**
48502          * The function used to validate alpha values
48503          * @param {String} value The value
48504          */
48505         'alpha' : function(v){
48506             return alpha.test(v);
48507         },
48508         /**
48509          * The error text to display when the alpha validation function returns false
48510          * @type String
48511          */
48512         'alphaText' : 'This field should only contain letters and _',
48513         /**
48514          * The keystroke filter mask to be applied on alpha input
48515          * @type RegExp
48516          */
48517         'alphaMask' : /[a-z_]/i,
48518
48519         /**
48520          * The function used to validate alphanumeric values
48521          * @param {String} value The value
48522          */
48523         'alphanum' : function(v){
48524             return alphanum.test(v);
48525         },
48526         /**
48527          * The error text to display when the alphanumeric validation function returns false
48528          * @type String
48529          */
48530         'alphanumText' : 'This field should only contain letters, numbers and _',
48531         /**
48532          * The keystroke filter mask to be applied on alphanumeric input
48533          * @type RegExp
48534          */
48535         'alphanumMask' : /[a-z0-9_]/i
48536     };
48537 }();//<script type="text/javascript">
48538
48539 /**
48540  * @class Roo.form.FCKeditor
48541  * @extends Roo.form.TextArea
48542  * Wrapper around the FCKEditor http://www.fckeditor.net
48543  * @constructor
48544  * Creates a new FCKeditor
48545  * @param {Object} config Configuration options
48546  */
48547 Roo.form.FCKeditor = function(config){
48548     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48549     this.addEvents({
48550          /**
48551          * @event editorinit
48552          * Fired when the editor is initialized - you can add extra handlers here..
48553          * @param {FCKeditor} this
48554          * @param {Object} the FCK object.
48555          */
48556         editorinit : true
48557     });
48558     
48559     
48560 };
48561 Roo.form.FCKeditor.editors = { };
48562 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48563 {
48564     //defaultAutoCreate : {
48565     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48566     //},
48567     // private
48568     /**
48569      * @cfg {Object} fck options - see fck manual for details.
48570      */
48571     fckconfig : false,
48572     
48573     /**
48574      * @cfg {Object} fck toolbar set (Basic or Default)
48575      */
48576     toolbarSet : 'Basic',
48577     /**
48578      * @cfg {Object} fck BasePath
48579      */ 
48580     basePath : '/fckeditor/',
48581     
48582     
48583     frame : false,
48584     
48585     value : '',
48586     
48587    
48588     onRender : function(ct, position)
48589     {
48590         if(!this.el){
48591             this.defaultAutoCreate = {
48592                 tag: "textarea",
48593                 style:"width:300px;height:60px;",
48594                 autocomplete: "new-password"
48595             };
48596         }
48597         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48598         /*
48599         if(this.grow){
48600             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48601             if(this.preventScrollbars){
48602                 this.el.setStyle("overflow", "hidden");
48603             }
48604             this.el.setHeight(this.growMin);
48605         }
48606         */
48607         //console.log('onrender' + this.getId() );
48608         Roo.form.FCKeditor.editors[this.getId()] = this;
48609          
48610
48611         this.replaceTextarea() ;
48612         
48613     },
48614     
48615     getEditor : function() {
48616         return this.fckEditor;
48617     },
48618     /**
48619      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48620      * @param {Mixed} value The value to set
48621      */
48622     
48623     
48624     setValue : function(value)
48625     {
48626         //console.log('setValue: ' + value);
48627         
48628         if(typeof(value) == 'undefined') { // not sure why this is happending...
48629             return;
48630         }
48631         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48632         
48633         //if(!this.el || !this.getEditor()) {
48634         //    this.value = value;
48635             //this.setValue.defer(100,this,[value]);    
48636         //    return;
48637         //} 
48638         
48639         if(!this.getEditor()) {
48640             return;
48641         }
48642         
48643         this.getEditor().SetData(value);
48644         
48645         //
48646
48647     },
48648
48649     /**
48650      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48651      * @return {Mixed} value The field value
48652      */
48653     getValue : function()
48654     {
48655         
48656         if (this.frame && this.frame.dom.style.display == 'none') {
48657             return Roo.form.FCKeditor.superclass.getValue.call(this);
48658         }
48659         
48660         if(!this.el || !this.getEditor()) {
48661            
48662            // this.getValue.defer(100,this); 
48663             return this.value;
48664         }
48665        
48666         
48667         var value=this.getEditor().GetData();
48668         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48669         return Roo.form.FCKeditor.superclass.getValue.call(this);
48670         
48671
48672     },
48673
48674     /**
48675      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48676      * @return {Mixed} value The field value
48677      */
48678     getRawValue : function()
48679     {
48680         if (this.frame && this.frame.dom.style.display == 'none') {
48681             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48682         }
48683         
48684         if(!this.el || !this.getEditor()) {
48685             //this.getRawValue.defer(100,this); 
48686             return this.value;
48687             return;
48688         }
48689         
48690         
48691         
48692         var value=this.getEditor().GetData();
48693         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48694         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48695          
48696     },
48697     
48698     setSize : function(w,h) {
48699         
48700         
48701         
48702         //if (this.frame && this.frame.dom.style.display == 'none') {
48703         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48704         //    return;
48705         //}
48706         //if(!this.el || !this.getEditor()) {
48707         //    this.setSize.defer(100,this, [w,h]); 
48708         //    return;
48709         //}
48710         
48711         
48712         
48713         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48714         
48715         this.frame.dom.setAttribute('width', w);
48716         this.frame.dom.setAttribute('height', h);
48717         this.frame.setSize(w,h);
48718         
48719     },
48720     
48721     toggleSourceEdit : function(value) {
48722         
48723       
48724          
48725         this.el.dom.style.display = value ? '' : 'none';
48726         this.frame.dom.style.display = value ?  'none' : '';
48727         
48728     },
48729     
48730     
48731     focus: function(tag)
48732     {
48733         if (this.frame.dom.style.display == 'none') {
48734             return Roo.form.FCKeditor.superclass.focus.call(this);
48735         }
48736         if(!this.el || !this.getEditor()) {
48737             this.focus.defer(100,this, [tag]); 
48738             return;
48739         }
48740         
48741         
48742         
48743         
48744         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48745         this.getEditor().Focus();
48746         if (tgs.length) {
48747             if (!this.getEditor().Selection.GetSelection()) {
48748                 this.focus.defer(100,this, [tag]); 
48749                 return;
48750             }
48751             
48752             
48753             var r = this.getEditor().EditorDocument.createRange();
48754             r.setStart(tgs[0],0);
48755             r.setEnd(tgs[0],0);
48756             this.getEditor().Selection.GetSelection().removeAllRanges();
48757             this.getEditor().Selection.GetSelection().addRange(r);
48758             this.getEditor().Focus();
48759         }
48760         
48761     },
48762     
48763     
48764     
48765     replaceTextarea : function()
48766     {
48767         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48768             return ;
48769         }
48770         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48771         //{
48772             // We must check the elements firstly using the Id and then the name.
48773         var oTextarea = document.getElementById( this.getId() );
48774         
48775         var colElementsByName = document.getElementsByName( this.getId() ) ;
48776          
48777         oTextarea.style.display = 'none' ;
48778
48779         if ( oTextarea.tabIndex ) {            
48780             this.TabIndex = oTextarea.tabIndex ;
48781         }
48782         
48783         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48784         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48785         this.frame = Roo.get(this.getId() + '___Frame')
48786     },
48787     
48788     _getConfigHtml : function()
48789     {
48790         var sConfig = '' ;
48791
48792         for ( var o in this.fckconfig ) {
48793             sConfig += sConfig.length > 0  ? '&amp;' : '';
48794             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48795         }
48796
48797         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48798     },
48799     
48800     
48801     _getIFrameHtml : function()
48802     {
48803         var sFile = 'fckeditor.html' ;
48804         /* no idea what this is about..
48805         try
48806         {
48807             if ( (/fcksource=true/i).test( window.top.location.search ) )
48808                 sFile = 'fckeditor.original.html' ;
48809         }
48810         catch (e) { 
48811         */
48812
48813         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48814         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48815         
48816         
48817         var html = '<iframe id="' + this.getId() +
48818             '___Frame" src="' + sLink +
48819             '" width="' + this.width +
48820             '" height="' + this.height + '"' +
48821             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48822             ' frameborder="0" scrolling="no"></iframe>' ;
48823
48824         return html ;
48825     },
48826     
48827     _insertHtmlBefore : function( html, element )
48828     {
48829         if ( element.insertAdjacentHTML )       {
48830             // IE
48831             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48832         } else { // Gecko
48833             var oRange = document.createRange() ;
48834             oRange.setStartBefore( element ) ;
48835             var oFragment = oRange.createContextualFragment( html );
48836             element.parentNode.insertBefore( oFragment, element ) ;
48837         }
48838     }
48839     
48840     
48841   
48842     
48843     
48844     
48845     
48846
48847 });
48848
48849 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48850
48851 function FCKeditor_OnComplete(editorInstance){
48852     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48853     f.fckEditor = editorInstance;
48854     //console.log("loaded");
48855     f.fireEvent('editorinit', f, editorInstance);
48856
48857   
48858
48859  
48860
48861
48862
48863
48864
48865
48866
48867
48868
48869
48870
48871
48872
48873
48874
48875 //<script type="text/javascript">
48876 /**
48877  * @class Roo.form.GridField
48878  * @extends Roo.form.Field
48879  * Embed a grid (or editable grid into a form)
48880  * STATUS ALPHA
48881  * 
48882  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48883  * it needs 
48884  * xgrid.store = Roo.data.Store
48885  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48886  * xgrid.store.reader = Roo.data.JsonReader 
48887  * 
48888  * 
48889  * @constructor
48890  * Creates a new GridField
48891  * @param {Object} config Configuration options
48892  */
48893 Roo.form.GridField = function(config){
48894     Roo.form.GridField.superclass.constructor.call(this, config);
48895      
48896 };
48897
48898 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48899     /**
48900      * @cfg {Number} width  - used to restrict width of grid..
48901      */
48902     width : 100,
48903     /**
48904      * @cfg {Number} height - used to restrict height of grid..
48905      */
48906     height : 50,
48907      /**
48908      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48909          * 
48910          *}
48911      */
48912     xgrid : false, 
48913     /**
48914      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48915      * {tag: "input", type: "checkbox", autocomplete: "off"})
48916      */
48917    // defaultAutoCreate : { tag: 'div' },
48918     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48919     /**
48920      * @cfg {String} addTitle Text to include for adding a title.
48921      */
48922     addTitle : false,
48923     //
48924     onResize : function(){
48925         Roo.form.Field.superclass.onResize.apply(this, arguments);
48926     },
48927
48928     initEvents : function(){
48929         // Roo.form.Checkbox.superclass.initEvents.call(this);
48930         // has no events...
48931        
48932     },
48933
48934
48935     getResizeEl : function(){
48936         return this.wrap;
48937     },
48938
48939     getPositionEl : function(){
48940         return this.wrap;
48941     },
48942
48943     // private
48944     onRender : function(ct, position){
48945         
48946         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48947         var style = this.style;
48948         delete this.style;
48949         
48950         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48951         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48952         this.viewEl = this.wrap.createChild({ tag: 'div' });
48953         if (style) {
48954             this.viewEl.applyStyles(style);
48955         }
48956         if (this.width) {
48957             this.viewEl.setWidth(this.width);
48958         }
48959         if (this.height) {
48960             this.viewEl.setHeight(this.height);
48961         }
48962         //if(this.inputValue !== undefined){
48963         //this.setValue(this.value);
48964         
48965         
48966         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48967         
48968         
48969         this.grid.render();
48970         this.grid.getDataSource().on('remove', this.refreshValue, this);
48971         this.grid.getDataSource().on('update', this.refreshValue, this);
48972         this.grid.on('afteredit', this.refreshValue, this);
48973  
48974     },
48975      
48976     
48977     /**
48978      * Sets the value of the item. 
48979      * @param {String} either an object  or a string..
48980      */
48981     setValue : function(v){
48982         //this.value = v;
48983         v = v || []; // empty set..
48984         // this does not seem smart - it really only affects memoryproxy grids..
48985         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48986             var ds = this.grid.getDataSource();
48987             // assumes a json reader..
48988             var data = {}
48989             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48990             ds.loadData( data);
48991         }
48992         // clear selection so it does not get stale.
48993         if (this.grid.sm) { 
48994             this.grid.sm.clearSelections();
48995         }
48996         
48997         Roo.form.GridField.superclass.setValue.call(this, v);
48998         this.refreshValue();
48999         // should load data in the grid really....
49000     },
49001     
49002     // private
49003     refreshValue: function() {
49004          var val = [];
49005         this.grid.getDataSource().each(function(r) {
49006             val.push(r.data);
49007         });
49008         this.el.dom.value = Roo.encode(val);
49009     }
49010     
49011      
49012     
49013     
49014 });/*
49015  * Based on:
49016  * Ext JS Library 1.1.1
49017  * Copyright(c) 2006-2007, Ext JS, LLC.
49018  *
49019  * Originally Released Under LGPL - original licence link has changed is not relivant.
49020  *
49021  * Fork - LGPL
49022  * <script type="text/javascript">
49023  */
49024 /**
49025  * @class Roo.form.DisplayField
49026  * @extends Roo.form.Field
49027  * A generic Field to display non-editable data.
49028  * @cfg {Boolean} closable (true|false) default false
49029  * @constructor
49030  * Creates a new Display Field item.
49031  * @param {Object} config Configuration options
49032  */
49033 Roo.form.DisplayField = function(config){
49034     Roo.form.DisplayField.superclass.constructor.call(this, config);
49035     
49036     this.addEvents({
49037         /**
49038          * @event close
49039          * Fires after the click the close btn
49040              * @param {Roo.form.DisplayField} this
49041              */
49042         close : true
49043     });
49044 };
49045
49046 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49047     inputType:      'hidden',
49048     allowBlank:     true,
49049     readOnly:         true,
49050     
49051  
49052     /**
49053      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49054      */
49055     focusClass : undefined,
49056     /**
49057      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49058      */
49059     fieldClass: 'x-form-field',
49060     
49061      /**
49062      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49063      */
49064     valueRenderer: undefined,
49065     
49066     width: 100,
49067     /**
49068      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49069      * {tag: "input", type: "checkbox", autocomplete: "off"})
49070      */
49071      
49072  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49073  
49074     closable : false,
49075     
49076     onResize : function(){
49077         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49078         
49079     },
49080
49081     initEvents : function(){
49082         // Roo.form.Checkbox.superclass.initEvents.call(this);
49083         // has no events...
49084         
49085         if(this.closable){
49086             this.closeEl.on('click', this.onClose, this);
49087         }
49088        
49089     },
49090
49091
49092     getResizeEl : function(){
49093         return this.wrap;
49094     },
49095
49096     getPositionEl : function(){
49097         return this.wrap;
49098     },
49099
49100     // private
49101     onRender : function(ct, position){
49102         
49103         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49104         //if(this.inputValue !== undefined){
49105         this.wrap = this.el.wrap();
49106         
49107         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49108         
49109         if(this.closable){
49110             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49111         }
49112         
49113         if (this.bodyStyle) {
49114             this.viewEl.applyStyles(this.bodyStyle);
49115         }
49116         //this.viewEl.setStyle('padding', '2px');
49117         
49118         this.setValue(this.value);
49119         
49120     },
49121 /*
49122     // private
49123     initValue : Roo.emptyFn,
49124
49125   */
49126
49127         // private
49128     onClick : function(){
49129         
49130     },
49131
49132     /**
49133      * Sets the checked state of the checkbox.
49134      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49135      */
49136     setValue : function(v){
49137         this.value = v;
49138         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49139         // this might be called before we have a dom element..
49140         if (!this.viewEl) {
49141             return;
49142         }
49143         this.viewEl.dom.innerHTML = html;
49144         Roo.form.DisplayField.superclass.setValue.call(this, v);
49145
49146     },
49147     
49148     onClose : function(e)
49149     {
49150         e.preventDefault();
49151         
49152         this.fireEvent('close', this);
49153     }
49154 });/*
49155  * 
49156  * Licence- LGPL
49157  * 
49158  */
49159
49160 /**
49161  * @class Roo.form.DayPicker
49162  * @extends Roo.form.Field
49163  * A Day picker show [M] [T] [W] ....
49164  * @constructor
49165  * Creates a new Day Picker
49166  * @param {Object} config Configuration options
49167  */
49168 Roo.form.DayPicker= function(config){
49169     Roo.form.DayPicker.superclass.constructor.call(this, config);
49170      
49171 };
49172
49173 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49174     /**
49175      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49176      */
49177     focusClass : undefined,
49178     /**
49179      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49180      */
49181     fieldClass: "x-form-field",
49182    
49183     /**
49184      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49185      * {tag: "input", type: "checkbox", autocomplete: "off"})
49186      */
49187     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49188     
49189    
49190     actionMode : 'viewEl', 
49191     //
49192     // private
49193  
49194     inputType : 'hidden',
49195     
49196      
49197     inputElement: false, // real input element?
49198     basedOn: false, // ????
49199     
49200     isFormField: true, // not sure where this is needed!!!!
49201
49202     onResize : function(){
49203         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49204         if(!this.boxLabel){
49205             this.el.alignTo(this.wrap, 'c-c');
49206         }
49207     },
49208
49209     initEvents : function(){
49210         Roo.form.Checkbox.superclass.initEvents.call(this);
49211         this.el.on("click", this.onClick,  this);
49212         this.el.on("change", this.onClick,  this);
49213     },
49214
49215
49216     getResizeEl : function(){
49217         return this.wrap;
49218     },
49219
49220     getPositionEl : function(){
49221         return this.wrap;
49222     },
49223
49224     
49225     // private
49226     onRender : function(ct, position){
49227         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49228        
49229         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49230         
49231         var r1 = '<table><tr>';
49232         var r2 = '<tr class="x-form-daypick-icons">';
49233         for (var i=0; i < 7; i++) {
49234             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49235             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49236         }
49237         
49238         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49239         viewEl.select('img').on('click', this.onClick, this);
49240         this.viewEl = viewEl;   
49241         
49242         
49243         // this will not work on Chrome!!!
49244         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49245         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49246         
49247         
49248           
49249
49250     },
49251
49252     // private
49253     initValue : Roo.emptyFn,
49254
49255     /**
49256      * Returns the checked state of the checkbox.
49257      * @return {Boolean} True if checked, else false
49258      */
49259     getValue : function(){
49260         return this.el.dom.value;
49261         
49262     },
49263
49264         // private
49265     onClick : function(e){ 
49266         //this.setChecked(!this.checked);
49267         Roo.get(e.target).toggleClass('x-menu-item-checked');
49268         this.refreshValue();
49269         //if(this.el.dom.checked != this.checked){
49270         //    this.setValue(this.el.dom.checked);
49271        // }
49272     },
49273     
49274     // private
49275     refreshValue : function()
49276     {
49277         var val = '';
49278         this.viewEl.select('img',true).each(function(e,i,n)  {
49279             val += e.is(".x-menu-item-checked") ? String(n) : '';
49280         });
49281         this.setValue(val, true);
49282     },
49283
49284     /**
49285      * Sets the checked state of the checkbox.
49286      * On is always based on a string comparison between inputValue and the param.
49287      * @param {Boolean/String} value - the value to set 
49288      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49289      */
49290     setValue : function(v,suppressEvent){
49291         if (!this.el.dom) {
49292             return;
49293         }
49294         var old = this.el.dom.value ;
49295         this.el.dom.value = v;
49296         if (suppressEvent) {
49297             return ;
49298         }
49299          
49300         // update display..
49301         this.viewEl.select('img',true).each(function(e,i,n)  {
49302             
49303             var on = e.is(".x-menu-item-checked");
49304             var newv = v.indexOf(String(n)) > -1;
49305             if (on != newv) {
49306                 e.toggleClass('x-menu-item-checked');
49307             }
49308             
49309         });
49310         
49311         
49312         this.fireEvent('change', this, v, old);
49313         
49314         
49315     },
49316    
49317     // handle setting of hidden value by some other method!!?!?
49318     setFromHidden: function()
49319     {
49320         if(!this.el){
49321             return;
49322         }
49323         //console.log("SET FROM HIDDEN");
49324         //alert('setFrom hidden');
49325         this.setValue(this.el.dom.value);
49326     },
49327     
49328     onDestroy : function()
49329     {
49330         if(this.viewEl){
49331             Roo.get(this.viewEl).remove();
49332         }
49333          
49334         Roo.form.DayPicker.superclass.onDestroy.call(this);
49335     }
49336
49337 });/*
49338  * RooJS Library 1.1.1
49339  * Copyright(c) 2008-2011  Alan Knowles
49340  *
49341  * License - LGPL
49342  */
49343  
49344
49345 /**
49346  * @class Roo.form.ComboCheck
49347  * @extends Roo.form.ComboBox
49348  * A combobox for multiple select items.
49349  *
49350  * FIXME - could do with a reset button..
49351  * 
49352  * @constructor
49353  * Create a new ComboCheck
49354  * @param {Object} config Configuration options
49355  */
49356 Roo.form.ComboCheck = function(config){
49357     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49358     // should verify some data...
49359     // like
49360     // hiddenName = required..
49361     // displayField = required
49362     // valudField == required
49363     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49364     var _t = this;
49365     Roo.each(req, function(e) {
49366         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49367             throw "Roo.form.ComboCheck : missing value for: " + e;
49368         }
49369     });
49370     
49371     
49372 };
49373
49374 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49375      
49376      
49377     editable : false,
49378      
49379     selectedClass: 'x-menu-item-checked', 
49380     
49381     // private
49382     onRender : function(ct, position){
49383         var _t = this;
49384         
49385         
49386         
49387         if(!this.tpl){
49388             var cls = 'x-combo-list';
49389
49390             
49391             this.tpl =  new Roo.Template({
49392                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49393                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49394                    '<span>{' + this.displayField + '}</span>' +
49395                     '</div>' 
49396                 
49397             });
49398         }
49399  
49400         
49401         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49402         this.view.singleSelect = false;
49403         this.view.multiSelect = true;
49404         this.view.toggleSelect = true;
49405         this.pageTb.add(new Roo.Toolbar.Fill(), {
49406             
49407             text: 'Done',
49408             handler: function()
49409             {
49410                 _t.collapse();
49411             }
49412         });
49413     },
49414     
49415     onViewOver : function(e, t){
49416         // do nothing...
49417         return;
49418         
49419     },
49420     
49421     onViewClick : function(doFocus,index){
49422         return;
49423         
49424     },
49425     select: function () {
49426         //Roo.log("SELECT CALLED");
49427     },
49428      
49429     selectByValue : function(xv, scrollIntoView){
49430         var ar = this.getValueArray();
49431         var sels = [];
49432         
49433         Roo.each(ar, function(v) {
49434             if(v === undefined || v === null){
49435                 return;
49436             }
49437             var r = this.findRecord(this.valueField, v);
49438             if(r){
49439                 sels.push(this.store.indexOf(r))
49440                 
49441             }
49442         },this);
49443         this.view.select(sels);
49444         return false;
49445     },
49446     
49447     
49448     
49449     onSelect : function(record, index){
49450        // Roo.log("onselect Called");
49451        // this is only called by the clear button now..
49452         this.view.clearSelections();
49453         this.setValue('[]');
49454         if (this.value != this.valueBefore) {
49455             this.fireEvent('change', this, this.value, this.valueBefore);
49456             this.valueBefore = this.value;
49457         }
49458     },
49459     getValueArray : function()
49460     {
49461         var ar = [] ;
49462         
49463         try {
49464             //Roo.log(this.value);
49465             if (typeof(this.value) == 'undefined') {
49466                 return [];
49467             }
49468             var ar = Roo.decode(this.value);
49469             return  ar instanceof Array ? ar : []; //?? valid?
49470             
49471         } catch(e) {
49472             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49473             return [];
49474         }
49475          
49476     },
49477     expand : function ()
49478     {
49479         
49480         Roo.form.ComboCheck.superclass.expand.call(this);
49481         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49482         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49483         
49484
49485     },
49486     
49487     collapse : function(){
49488         Roo.form.ComboCheck.superclass.collapse.call(this);
49489         var sl = this.view.getSelectedIndexes();
49490         var st = this.store;
49491         var nv = [];
49492         var tv = [];
49493         var r;
49494         Roo.each(sl, function(i) {
49495             r = st.getAt(i);
49496             nv.push(r.get(this.valueField));
49497         },this);
49498         this.setValue(Roo.encode(nv));
49499         if (this.value != this.valueBefore) {
49500
49501             this.fireEvent('change', this, this.value, this.valueBefore);
49502             this.valueBefore = this.value;
49503         }
49504         
49505     },
49506     
49507     setValue : function(v){
49508         // Roo.log(v);
49509         this.value = v;
49510         
49511         var vals = this.getValueArray();
49512         var tv = [];
49513         Roo.each(vals, function(k) {
49514             var r = this.findRecord(this.valueField, k);
49515             if(r){
49516                 tv.push(r.data[this.displayField]);
49517             }else if(this.valueNotFoundText !== undefined){
49518                 tv.push( this.valueNotFoundText );
49519             }
49520         },this);
49521        // Roo.log(tv);
49522         
49523         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49524         this.hiddenField.value = v;
49525         this.value = v;
49526     }
49527     
49528 });/*
49529  * Based on:
49530  * Ext JS Library 1.1.1
49531  * Copyright(c) 2006-2007, Ext JS, LLC.
49532  *
49533  * Originally Released Under LGPL - original licence link has changed is not relivant.
49534  *
49535  * Fork - LGPL
49536  * <script type="text/javascript">
49537  */
49538  
49539 /**
49540  * @class Roo.form.Signature
49541  * @extends Roo.form.Field
49542  * Signature field.  
49543  * @constructor
49544  * 
49545  * @param {Object} config Configuration options
49546  */
49547
49548 Roo.form.Signature = function(config){
49549     Roo.form.Signature.superclass.constructor.call(this, config);
49550     
49551     this.addEvents({// not in used??
49552          /**
49553          * @event confirm
49554          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49555              * @param {Roo.form.Signature} combo This combo box
49556              */
49557         'confirm' : true,
49558         /**
49559          * @event reset
49560          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49561              * @param {Roo.form.ComboBox} combo This combo box
49562              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49563              */
49564         'reset' : true
49565     });
49566 };
49567
49568 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49569     /**
49570      * @cfg {Object} labels Label to use when rendering a form.
49571      * defaults to 
49572      * labels : { 
49573      *      clear : "Clear",
49574      *      confirm : "Confirm"
49575      *  }
49576      */
49577     labels : { 
49578         clear : "Clear",
49579         confirm : "Confirm"
49580     },
49581     /**
49582      * @cfg {Number} width The signature panel width (defaults to 300)
49583      */
49584     width: 300,
49585     /**
49586      * @cfg {Number} height The signature panel height (defaults to 100)
49587      */
49588     height : 100,
49589     /**
49590      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49591      */
49592     allowBlank : false,
49593     
49594     //private
49595     // {Object} signPanel The signature SVG panel element (defaults to {})
49596     signPanel : {},
49597     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49598     isMouseDown : false,
49599     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49600     isConfirmed : false,
49601     // {String} signatureTmp SVG mapping string (defaults to empty string)
49602     signatureTmp : '',
49603     
49604     
49605     defaultAutoCreate : { // modified by initCompnoent..
49606         tag: "input",
49607         type:"hidden"
49608     },
49609
49610     // private
49611     onRender : function(ct, position){
49612         
49613         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49614         
49615         this.wrap = this.el.wrap({
49616             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49617         });
49618         
49619         this.createToolbar(this);
49620         this.signPanel = this.wrap.createChild({
49621                 tag: 'div',
49622                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49623             }, this.el
49624         );
49625             
49626         this.svgID = Roo.id();
49627         this.svgEl = this.signPanel.createChild({
49628               xmlns : 'http://www.w3.org/2000/svg',
49629               tag : 'svg',
49630               id : this.svgID + "-svg",
49631               width: this.width,
49632               height: this.height,
49633               viewBox: '0 0 '+this.width+' '+this.height,
49634               cn : [
49635                 {
49636                     tag: "rect",
49637                     id: this.svgID + "-svg-r",
49638                     width: this.width,
49639                     height: this.height,
49640                     fill: "#ffa"
49641                 },
49642                 {
49643                     tag: "line",
49644                     id: this.svgID + "-svg-l",
49645                     x1: "0", // start
49646                     y1: (this.height*0.8), // start set the line in 80% of height
49647                     x2: this.width, // end
49648                     y2: (this.height*0.8), // end set the line in 80% of height
49649                     'stroke': "#666",
49650                     'stroke-width': "1",
49651                     'stroke-dasharray': "3",
49652                     'shape-rendering': "crispEdges",
49653                     'pointer-events': "none"
49654                 },
49655                 {
49656                     tag: "path",
49657                     id: this.svgID + "-svg-p",
49658                     'stroke': "navy",
49659                     'stroke-width': "3",
49660                     'fill': "none",
49661                     'pointer-events': 'none'
49662                 }
49663               ]
49664         });
49665         this.createSVG();
49666         this.svgBox = this.svgEl.dom.getScreenCTM();
49667     },
49668     createSVG : function(){ 
49669         var svg = this.signPanel;
49670         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49671         var t = this;
49672
49673         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49674         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49675         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49676         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49677         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49678         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49679         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49680         
49681     },
49682     isTouchEvent : function(e){
49683         return e.type.match(/^touch/);
49684     },
49685     getCoords : function (e) {
49686         var pt    = this.svgEl.dom.createSVGPoint();
49687         pt.x = e.clientX; 
49688         pt.y = e.clientY;
49689         if (this.isTouchEvent(e)) {
49690             pt.x =  e.targetTouches[0].clientX;
49691             pt.y = e.targetTouches[0].clientY;
49692         }
49693         var a = this.svgEl.dom.getScreenCTM();
49694         var b = a.inverse();
49695         var mx = pt.matrixTransform(b);
49696         return mx.x + ',' + mx.y;
49697     },
49698     //mouse event headler 
49699     down : function (e) {
49700         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49701         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49702         
49703         this.isMouseDown = true;
49704         
49705         e.preventDefault();
49706     },
49707     move : function (e) {
49708         if (this.isMouseDown) {
49709             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49710             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49711         }
49712         
49713         e.preventDefault();
49714     },
49715     up : function (e) {
49716         this.isMouseDown = false;
49717         var sp = this.signatureTmp.split(' ');
49718         
49719         if(sp.length > 1){
49720             if(!sp[sp.length-2].match(/^L/)){
49721                 sp.pop();
49722                 sp.pop();
49723                 sp.push("");
49724                 this.signatureTmp = sp.join(" ");
49725             }
49726         }
49727         if(this.getValue() != this.signatureTmp){
49728             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49729             this.isConfirmed = false;
49730         }
49731         e.preventDefault();
49732     },
49733     
49734     /**
49735      * Protected method that will not generally be called directly. It
49736      * is called when the editor creates its toolbar. Override this method if you need to
49737      * add custom toolbar buttons.
49738      * @param {HtmlEditor} editor
49739      */
49740     createToolbar : function(editor){
49741          function btn(id, toggle, handler){
49742             var xid = fid + '-'+ id ;
49743             return {
49744                 id : xid,
49745                 cmd : id,
49746                 cls : 'x-btn-icon x-edit-'+id,
49747                 enableToggle:toggle !== false,
49748                 scope: editor, // was editor...
49749                 handler:handler||editor.relayBtnCmd,
49750                 clickEvent:'mousedown',
49751                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49752                 tabIndex:-1
49753             };
49754         }
49755         
49756         
49757         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49758         this.tb = tb;
49759         this.tb.add(
49760            {
49761                 cls : ' x-signature-btn x-signature-'+id,
49762                 scope: editor, // was editor...
49763                 handler: this.reset,
49764                 clickEvent:'mousedown',
49765                 text: this.labels.clear
49766             },
49767             {
49768                  xtype : 'Fill',
49769                  xns: Roo.Toolbar
49770             }, 
49771             {
49772                 cls : '  x-signature-btn x-signature-'+id,
49773                 scope: editor, // was editor...
49774                 handler: this.confirmHandler,
49775                 clickEvent:'mousedown',
49776                 text: this.labels.confirm
49777             }
49778         );
49779     
49780     },
49781     //public
49782     /**
49783      * when user is clicked confirm then show this image.....
49784      * 
49785      * @return {String} Image Data URI
49786      */
49787     getImageDataURI : function(){
49788         var svg = this.svgEl.dom.parentNode.innerHTML;
49789         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49790         return src; 
49791     },
49792     /**
49793      * 
49794      * @return {Boolean} this.isConfirmed
49795      */
49796     getConfirmed : function(){
49797         return this.isConfirmed;
49798     },
49799     /**
49800      * 
49801      * @return {Number} this.width
49802      */
49803     getWidth : function(){
49804         return this.width;
49805     },
49806     /**
49807      * 
49808      * @return {Number} this.height
49809      */
49810     getHeight : function(){
49811         return this.height;
49812     },
49813     // private
49814     getSignature : function(){
49815         return this.signatureTmp;
49816     },
49817     // private
49818     reset : function(){
49819         this.signatureTmp = '';
49820         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49821         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49822         this.isConfirmed = false;
49823         Roo.form.Signature.superclass.reset.call(this);
49824     },
49825     setSignature : function(s){
49826         this.signatureTmp = s;
49827         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49828         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49829         this.setValue(s);
49830         this.isConfirmed = false;
49831         Roo.form.Signature.superclass.reset.call(this);
49832     }, 
49833     test : function(){
49834 //        Roo.log(this.signPanel.dom.contentWindow.up())
49835     },
49836     //private
49837     setConfirmed : function(){
49838         
49839         
49840         
49841 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49842     },
49843     // private
49844     confirmHandler : function(){
49845         if(!this.getSignature()){
49846             return;
49847         }
49848         
49849         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49850         this.setValue(this.getSignature());
49851         this.isConfirmed = true;
49852         
49853         this.fireEvent('confirm', this);
49854     },
49855     // private
49856     // Subclasses should provide the validation implementation by overriding this
49857     validateValue : function(value){
49858         if(this.allowBlank){
49859             return true;
49860         }
49861         
49862         if(this.isConfirmed){
49863             return true;
49864         }
49865         return false;
49866     }
49867 });/*
49868  * Based on:
49869  * Ext JS Library 1.1.1
49870  * Copyright(c) 2006-2007, Ext JS, LLC.
49871  *
49872  * Originally Released Under LGPL - original licence link has changed is not relivant.
49873  *
49874  * Fork - LGPL
49875  * <script type="text/javascript">
49876  */
49877  
49878
49879 /**
49880  * @class Roo.form.ComboBox
49881  * @extends Roo.form.TriggerField
49882  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49883  * @constructor
49884  * Create a new ComboBox.
49885  * @param {Object} config Configuration options
49886  */
49887 Roo.form.Select = function(config){
49888     Roo.form.Select.superclass.constructor.call(this, config);
49889      
49890 };
49891
49892 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49893     /**
49894      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49895      */
49896     /**
49897      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49898      * rendering into an Roo.Editor, defaults to false)
49899      */
49900     /**
49901      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49902      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49903      */
49904     /**
49905      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49906      */
49907     /**
49908      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49909      * the dropdown list (defaults to undefined, with no header element)
49910      */
49911
49912      /**
49913      * @cfg {String/Roo.Template} tpl The template to use to render the output
49914      */
49915      
49916     // private
49917     defaultAutoCreate : {tag: "select"  },
49918     /**
49919      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49920      */
49921     listWidth: undefined,
49922     /**
49923      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49924      * mode = 'remote' or 'text' if mode = 'local')
49925      */
49926     displayField: undefined,
49927     /**
49928      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49929      * mode = 'remote' or 'value' if mode = 'local'). 
49930      * Note: use of a valueField requires the user make a selection
49931      * in order for a value to be mapped.
49932      */
49933     valueField: undefined,
49934     
49935     
49936     /**
49937      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49938      * field's data value (defaults to the underlying DOM element's name)
49939      */
49940     hiddenName: undefined,
49941     /**
49942      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49943      */
49944     listClass: '',
49945     /**
49946      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49947      */
49948     selectedClass: 'x-combo-selected',
49949     /**
49950      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49951      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49952      * which displays a downward arrow icon).
49953      */
49954     triggerClass : 'x-form-arrow-trigger',
49955     /**
49956      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49957      */
49958     shadow:'sides',
49959     /**
49960      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49961      * anchor positions (defaults to 'tl-bl')
49962      */
49963     listAlign: 'tl-bl?',
49964     /**
49965      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49966      */
49967     maxHeight: 300,
49968     /**
49969      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49970      * query specified by the allQuery config option (defaults to 'query')
49971      */
49972     triggerAction: 'query',
49973     /**
49974      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49975      * (defaults to 4, does not apply if editable = false)
49976      */
49977     minChars : 4,
49978     /**
49979      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49980      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49981      */
49982     typeAhead: false,
49983     /**
49984      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49985      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49986      */
49987     queryDelay: 500,
49988     /**
49989      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49990      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49991      */
49992     pageSize: 0,
49993     /**
49994      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49995      * when editable = true (defaults to false)
49996      */
49997     selectOnFocus:false,
49998     /**
49999      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50000      */
50001     queryParam: 'query',
50002     /**
50003      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50004      * when mode = 'remote' (defaults to 'Loading...')
50005      */
50006     loadingText: 'Loading...',
50007     /**
50008      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50009      */
50010     resizable: false,
50011     /**
50012      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50013      */
50014     handleHeight : 8,
50015     /**
50016      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50017      * traditional select (defaults to true)
50018      */
50019     editable: true,
50020     /**
50021      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50022      */
50023     allQuery: '',
50024     /**
50025      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50026      */
50027     mode: 'remote',
50028     /**
50029      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50030      * listWidth has a higher value)
50031      */
50032     minListWidth : 70,
50033     /**
50034      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50035      * allow the user to set arbitrary text into the field (defaults to false)
50036      */
50037     forceSelection:false,
50038     /**
50039      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50040      * if typeAhead = true (defaults to 250)
50041      */
50042     typeAheadDelay : 250,
50043     /**
50044      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50045      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50046      */
50047     valueNotFoundText : undefined,
50048     
50049     /**
50050      * @cfg {String} defaultValue The value displayed after loading the store.
50051      */
50052     defaultValue: '',
50053     
50054     /**
50055      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50056      */
50057     blockFocus : false,
50058     
50059     /**
50060      * @cfg {Boolean} disableClear Disable showing of clear button.
50061      */
50062     disableClear : false,
50063     /**
50064      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50065      */
50066     alwaysQuery : false,
50067     
50068     //private
50069     addicon : false,
50070     editicon: false,
50071     
50072     // element that contains real text value.. (when hidden is used..)
50073      
50074     // private
50075     onRender : function(ct, position){
50076         Roo.form.Field.prototype.onRender.call(this, ct, position);
50077         
50078         if(this.store){
50079             this.store.on('beforeload', this.onBeforeLoad, this);
50080             this.store.on('load', this.onLoad, this);
50081             this.store.on('loadexception', this.onLoadException, this);
50082             this.store.load({});
50083         }
50084         
50085         
50086         
50087     },
50088
50089     // private
50090     initEvents : function(){
50091         //Roo.form.ComboBox.superclass.initEvents.call(this);
50092  
50093     },
50094
50095     onDestroy : function(){
50096        
50097         if(this.store){
50098             this.store.un('beforeload', this.onBeforeLoad, this);
50099             this.store.un('load', this.onLoad, this);
50100             this.store.un('loadexception', this.onLoadException, this);
50101         }
50102         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50103     },
50104
50105     // private
50106     fireKey : function(e){
50107         if(e.isNavKeyPress() && !this.list.isVisible()){
50108             this.fireEvent("specialkey", this, e);
50109         }
50110     },
50111
50112     // private
50113     onResize: function(w, h){
50114         
50115         return; 
50116     
50117         
50118     },
50119
50120     /**
50121      * Allow or prevent the user from directly editing the field text.  If false is passed,
50122      * the user will only be able to select from the items defined in the dropdown list.  This method
50123      * is the runtime equivalent of setting the 'editable' config option at config time.
50124      * @param {Boolean} value True to allow the user to directly edit the field text
50125      */
50126     setEditable : function(value){
50127          
50128     },
50129
50130     // private
50131     onBeforeLoad : function(){
50132         
50133         Roo.log("Select before load");
50134         return;
50135     
50136         this.innerList.update(this.loadingText ?
50137                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50138         //this.restrictHeight();
50139         this.selectedIndex = -1;
50140     },
50141
50142     // private
50143     onLoad : function(){
50144
50145     
50146         var dom = this.el.dom;
50147         dom.innerHTML = '';
50148          var od = dom.ownerDocument;
50149          
50150         if (this.emptyText) {
50151             var op = od.createElement('option');
50152             op.setAttribute('value', '');
50153             op.innerHTML = String.format('{0}', this.emptyText);
50154             dom.appendChild(op);
50155         }
50156         if(this.store.getCount() > 0){
50157            
50158             var vf = this.valueField;
50159             var df = this.displayField;
50160             this.store.data.each(function(r) {
50161                 // which colmsn to use... testing - cdoe / title..
50162                 var op = od.createElement('option');
50163                 op.setAttribute('value', r.data[vf]);
50164                 op.innerHTML = String.format('{0}', r.data[df]);
50165                 dom.appendChild(op);
50166             });
50167             if (typeof(this.defaultValue != 'undefined')) {
50168                 this.setValue(this.defaultValue);
50169             }
50170             
50171              
50172         }else{
50173             //this.onEmptyResults();
50174         }
50175         //this.el.focus();
50176     },
50177     // private
50178     onLoadException : function()
50179     {
50180         dom.innerHTML = '';
50181             
50182         Roo.log("Select on load exception");
50183         return;
50184     
50185         this.collapse();
50186         Roo.log(this.store.reader.jsonData);
50187         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50188             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50189         }
50190         
50191         
50192     },
50193     // private
50194     onTypeAhead : function(){
50195          
50196     },
50197
50198     // private
50199     onSelect : function(record, index){
50200         Roo.log('on select?');
50201         return;
50202         if(this.fireEvent('beforeselect', this, record, index) !== false){
50203             this.setFromData(index > -1 ? record.data : false);
50204             this.collapse();
50205             this.fireEvent('select', this, record, index);
50206         }
50207     },
50208
50209     /**
50210      * Returns the currently selected field value or empty string if no value is set.
50211      * @return {String} value The selected value
50212      */
50213     getValue : function(){
50214         var dom = this.el.dom;
50215         this.value = dom.options[dom.selectedIndex].value;
50216         return this.value;
50217         
50218     },
50219
50220     /**
50221      * Clears any text/value currently set in the field
50222      */
50223     clearValue : function(){
50224         this.value = '';
50225         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50226         
50227     },
50228
50229     /**
50230      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50231      * will be displayed in the field.  If the value does not match the data value of an existing item,
50232      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50233      * Otherwise the field will be blank (although the value will still be set).
50234      * @param {String} value The value to match
50235      */
50236     setValue : function(v){
50237         var d = this.el.dom;
50238         for (var i =0; i < d.options.length;i++) {
50239             if (v == d.options[i].value) {
50240                 d.selectedIndex = i;
50241                 this.value = v;
50242                 return;
50243             }
50244         }
50245         this.clearValue();
50246     },
50247     /**
50248      * @property {Object} the last set data for the element
50249      */
50250     
50251     lastData : false,
50252     /**
50253      * Sets the value of the field based on a object which is related to the record format for the store.
50254      * @param {Object} value the value to set as. or false on reset?
50255      */
50256     setFromData : function(o){
50257         Roo.log('setfrom data?');
50258          
50259         
50260         
50261     },
50262     // private
50263     reset : function(){
50264         this.clearValue();
50265     },
50266     // private
50267     findRecord : function(prop, value){
50268         
50269         return false;
50270     
50271         var record;
50272         if(this.store.getCount() > 0){
50273             this.store.each(function(r){
50274                 if(r.data[prop] == value){
50275                     record = r;
50276                     return false;
50277                 }
50278                 return true;
50279             });
50280         }
50281         return record;
50282     },
50283     
50284     getName: function()
50285     {
50286         // returns hidden if it's set..
50287         if (!this.rendered) {return ''};
50288         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50289         
50290     },
50291      
50292
50293     
50294
50295     // private
50296     onEmptyResults : function(){
50297         Roo.log('empty results');
50298         //this.collapse();
50299     },
50300
50301     /**
50302      * Returns true if the dropdown list is expanded, else false.
50303      */
50304     isExpanded : function(){
50305         return false;
50306     },
50307
50308     /**
50309      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50310      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50311      * @param {String} value The data value of the item to select
50312      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50313      * selected item if it is not currently in view (defaults to true)
50314      * @return {Boolean} True if the value matched an item in the list, else false
50315      */
50316     selectByValue : function(v, scrollIntoView){
50317         Roo.log('select By Value');
50318         return false;
50319     
50320         if(v !== undefined && v !== null){
50321             var r = this.findRecord(this.valueField || this.displayField, v);
50322             if(r){
50323                 this.select(this.store.indexOf(r), scrollIntoView);
50324                 return true;
50325             }
50326         }
50327         return false;
50328     },
50329
50330     /**
50331      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50332      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50333      * @param {Number} index The zero-based index of the list item to select
50334      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50335      * selected item if it is not currently in view (defaults to true)
50336      */
50337     select : function(index, scrollIntoView){
50338         Roo.log('select ');
50339         return  ;
50340         
50341         this.selectedIndex = index;
50342         this.view.select(index);
50343         if(scrollIntoView !== false){
50344             var el = this.view.getNode(index);
50345             if(el){
50346                 this.innerList.scrollChildIntoView(el, false);
50347             }
50348         }
50349     },
50350
50351       
50352
50353     // private
50354     validateBlur : function(){
50355         
50356         return;
50357         
50358     },
50359
50360     // private
50361     initQuery : function(){
50362         this.doQuery(this.getRawValue());
50363     },
50364
50365     // private
50366     doForce : function(){
50367         if(this.el.dom.value.length > 0){
50368             this.el.dom.value =
50369                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50370              
50371         }
50372     },
50373
50374     /**
50375      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50376      * query allowing the query action to be canceled if needed.
50377      * @param {String} query The SQL query to execute
50378      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50379      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50380      * saved in the current store (defaults to false)
50381      */
50382     doQuery : function(q, forceAll){
50383         
50384         Roo.log('doQuery?');
50385         if(q === undefined || q === null){
50386             q = '';
50387         }
50388         var qe = {
50389             query: q,
50390             forceAll: forceAll,
50391             combo: this,
50392             cancel:false
50393         };
50394         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50395             return false;
50396         }
50397         q = qe.query;
50398         forceAll = qe.forceAll;
50399         if(forceAll === true || (q.length >= this.minChars)){
50400             if(this.lastQuery != q || this.alwaysQuery){
50401                 this.lastQuery = q;
50402                 if(this.mode == 'local'){
50403                     this.selectedIndex = -1;
50404                     if(forceAll){
50405                         this.store.clearFilter();
50406                     }else{
50407                         this.store.filter(this.displayField, q);
50408                     }
50409                     this.onLoad();
50410                 }else{
50411                     this.store.baseParams[this.queryParam] = q;
50412                     this.store.load({
50413                         params: this.getParams(q)
50414                     });
50415                     this.expand();
50416                 }
50417             }else{
50418                 this.selectedIndex = -1;
50419                 this.onLoad();   
50420             }
50421         }
50422     },
50423
50424     // private
50425     getParams : function(q){
50426         var p = {};
50427         //p[this.queryParam] = q;
50428         if(this.pageSize){
50429             p.start = 0;
50430             p.limit = this.pageSize;
50431         }
50432         return p;
50433     },
50434
50435     /**
50436      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50437      */
50438     collapse : function(){
50439         
50440     },
50441
50442     // private
50443     collapseIf : function(e){
50444         
50445     },
50446
50447     /**
50448      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50449      */
50450     expand : function(){
50451         
50452     } ,
50453
50454     // private
50455      
50456
50457     /** 
50458     * @cfg {Boolean} grow 
50459     * @hide 
50460     */
50461     /** 
50462     * @cfg {Number} growMin 
50463     * @hide 
50464     */
50465     /** 
50466     * @cfg {Number} growMax 
50467     * @hide 
50468     */
50469     /**
50470      * @hide
50471      * @method autoSize
50472      */
50473     
50474     setWidth : function()
50475     {
50476         
50477     },
50478     getResizeEl : function(){
50479         return this.el;
50480     }
50481 });//<script type="text/javasscript">
50482  
50483
50484 /**
50485  * @class Roo.DDView
50486  * A DnD enabled version of Roo.View.
50487  * @param {Element/String} container The Element in which to create the View.
50488  * @param {String} tpl The template string used to create the markup for each element of the View
50489  * @param {Object} config The configuration properties. These include all the config options of
50490  * {@link Roo.View} plus some specific to this class.<br>
50491  * <p>
50492  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50493  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50494  * <p>
50495  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50496 .x-view-drag-insert-above {
50497         border-top:1px dotted #3366cc;
50498 }
50499 .x-view-drag-insert-below {
50500         border-bottom:1px dotted #3366cc;
50501 }
50502 </code></pre>
50503  * 
50504  */
50505  
50506 Roo.DDView = function(container, tpl, config) {
50507     Roo.DDView.superclass.constructor.apply(this, arguments);
50508     this.getEl().setStyle("outline", "0px none");
50509     this.getEl().unselectable();
50510     if (this.dragGroup) {
50511                 this.setDraggable(this.dragGroup.split(","));
50512     }
50513     if (this.dropGroup) {
50514                 this.setDroppable(this.dropGroup.split(","));
50515     }
50516     if (this.deletable) {
50517         this.setDeletable();
50518     }
50519     this.isDirtyFlag = false;
50520         this.addEvents({
50521                 "drop" : true
50522         });
50523 };
50524
50525 Roo.extend(Roo.DDView, Roo.View, {
50526 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50527 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50528 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50529 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50530
50531         isFormField: true,
50532
50533         reset: Roo.emptyFn,
50534         
50535         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50536
50537         validate: function() {
50538                 return true;
50539         },
50540         
50541         destroy: function() {
50542                 this.purgeListeners();
50543                 this.getEl.removeAllListeners();
50544                 this.getEl().remove();
50545                 if (this.dragZone) {
50546                         if (this.dragZone.destroy) {
50547                                 this.dragZone.destroy();
50548                         }
50549                 }
50550                 if (this.dropZone) {
50551                         if (this.dropZone.destroy) {
50552                                 this.dropZone.destroy();
50553                         }
50554                 }
50555         },
50556
50557 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50558         getName: function() {
50559                 return this.name;
50560         },
50561
50562 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50563         setValue: function(v) {
50564                 if (!this.store) {
50565                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50566                 }
50567                 var data = {};
50568                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50569                 this.store.proxy = new Roo.data.MemoryProxy(data);
50570                 this.store.load();
50571         },
50572
50573 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50574         getValue: function() {
50575                 var result = '(';
50576                 this.store.each(function(rec) {
50577                         result += rec.id + ',';
50578                 });
50579                 return result.substr(0, result.length - 1) + ')';
50580         },
50581         
50582         getIds: function() {
50583                 var i = 0, result = new Array(this.store.getCount());
50584                 this.store.each(function(rec) {
50585                         result[i++] = rec.id;
50586                 });
50587                 return result;
50588         },
50589         
50590         isDirty: function() {
50591                 return this.isDirtyFlag;
50592         },
50593
50594 /**
50595  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50596  *      whole Element becomes the target, and this causes the drop gesture to append.
50597  */
50598     getTargetFromEvent : function(e) {
50599                 var target = e.getTarget();
50600                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50601                 target = target.parentNode;
50602                 }
50603                 if (!target) {
50604                         target = this.el.dom.lastChild || this.el.dom;
50605                 }
50606                 return target;
50607     },
50608
50609 /**
50610  *      Create the drag data which consists of an object which has the property "ddel" as
50611  *      the drag proxy element. 
50612  */
50613     getDragData : function(e) {
50614         var target = this.findItemFromChild(e.getTarget());
50615                 if(target) {
50616                         this.handleSelection(e);
50617                         var selNodes = this.getSelectedNodes();
50618             var dragData = {
50619                 source: this,
50620                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50621                 nodes: selNodes,
50622                 records: []
50623                         };
50624                         var selectedIndices = this.getSelectedIndexes();
50625                         for (var i = 0; i < selectedIndices.length; i++) {
50626                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50627                         }
50628                         if (selNodes.length == 1) {
50629                                 dragData.ddel = target.cloneNode(true); // the div element
50630                         } else {
50631                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50632                                 div.className = 'multi-proxy';
50633                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50634                                         div.appendChild(selNodes[i].cloneNode(true));
50635                                 }
50636                                 dragData.ddel = div;
50637                         }
50638             //console.log(dragData)
50639             //console.log(dragData.ddel.innerHTML)
50640                         return dragData;
50641                 }
50642         //console.log('nodragData')
50643                 return false;
50644     },
50645     
50646 /**     Specify to which ddGroup items in this DDView may be dragged. */
50647     setDraggable: function(ddGroup) {
50648         if (ddGroup instanceof Array) {
50649                 Roo.each(ddGroup, this.setDraggable, this);
50650                 return;
50651         }
50652         if (this.dragZone) {
50653                 this.dragZone.addToGroup(ddGroup);
50654         } else {
50655                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50656                                 containerScroll: true,
50657                                 ddGroup: ddGroup 
50658
50659                         });
50660 //                      Draggability implies selection. DragZone's mousedown selects the element.
50661                         if (!this.multiSelect) { this.singleSelect = true; }
50662
50663 //                      Wire the DragZone's handlers up to methods in *this*
50664                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50665                 }
50666     },
50667
50668 /**     Specify from which ddGroup this DDView accepts drops. */
50669     setDroppable: function(ddGroup) {
50670         if (ddGroup instanceof Array) {
50671                 Roo.each(ddGroup, this.setDroppable, this);
50672                 return;
50673         }
50674         if (this.dropZone) {
50675                 this.dropZone.addToGroup(ddGroup);
50676         } else {
50677                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50678                                 containerScroll: true,
50679                                 ddGroup: ddGroup
50680                         });
50681
50682 //                      Wire the DropZone's handlers up to methods in *this*
50683                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50684                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50685                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50686                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50687                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50688                 }
50689     },
50690
50691 /**     Decide whether to drop above or below a View node. */
50692     getDropPoint : function(e, n, dd){
50693         if (n == this.el.dom) { return "above"; }
50694                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50695                 var c = t + (b - t) / 2;
50696                 var y = Roo.lib.Event.getPageY(e);
50697                 if(y <= c) {
50698                         return "above";
50699                 }else{
50700                         return "below";
50701                 }
50702     },
50703
50704     onNodeEnter : function(n, dd, e, data){
50705                 return false;
50706     },
50707     
50708     onNodeOver : function(n, dd, e, data){
50709                 var pt = this.getDropPoint(e, n, dd);
50710                 // set the insert point style on the target node
50711                 var dragElClass = this.dropNotAllowed;
50712                 if (pt) {
50713                         var targetElClass;
50714                         if (pt == "above"){
50715                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50716                                 targetElClass = "x-view-drag-insert-above";
50717                         } else {
50718                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50719                                 targetElClass = "x-view-drag-insert-below";
50720                         }
50721                         if (this.lastInsertClass != targetElClass){
50722                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50723                                 this.lastInsertClass = targetElClass;
50724                         }
50725                 }
50726                 return dragElClass;
50727         },
50728
50729     onNodeOut : function(n, dd, e, data){
50730                 this.removeDropIndicators(n);
50731     },
50732
50733     onNodeDrop : function(n, dd, e, data){
50734         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50735                 return false;
50736         }
50737         var pt = this.getDropPoint(e, n, dd);
50738                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50739                 if (pt == "below") { insertAt++; }
50740                 for (var i = 0; i < data.records.length; i++) {
50741                         var r = data.records[i];
50742                         var dup = this.store.getById(r.id);
50743                         if (dup && (dd != this.dragZone)) {
50744                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50745                         } else {
50746                                 if (data.copy) {
50747                                         this.store.insert(insertAt++, r.copy());
50748                                 } else {
50749                                         data.source.isDirtyFlag = true;
50750                                         r.store.remove(r);
50751                                         this.store.insert(insertAt++, r);
50752                                 }
50753                                 this.isDirtyFlag = true;
50754                         }
50755                 }
50756                 this.dragZone.cachedTarget = null;
50757                 return true;
50758     },
50759
50760     removeDropIndicators : function(n){
50761                 if(n){
50762                         Roo.fly(n).removeClass([
50763                                 "x-view-drag-insert-above",
50764                                 "x-view-drag-insert-below"]);
50765                         this.lastInsertClass = "_noclass";
50766                 }
50767     },
50768
50769 /**
50770  *      Utility method. Add a delete option to the DDView's context menu.
50771  *      @param {String} imageUrl The URL of the "delete" icon image.
50772  */
50773         setDeletable: function(imageUrl) {
50774                 if (!this.singleSelect && !this.multiSelect) {
50775                         this.singleSelect = true;
50776                 }
50777                 var c = this.getContextMenu();
50778                 this.contextMenu.on("itemclick", function(item) {
50779                         switch (item.id) {
50780                                 case "delete":
50781                                         this.remove(this.getSelectedIndexes());
50782                                         break;
50783                         }
50784                 }, this);
50785                 this.contextMenu.add({
50786                         icon: imageUrl,
50787                         id: "delete",
50788                         text: 'Delete'
50789                 });
50790         },
50791         
50792 /**     Return the context menu for this DDView. */
50793         getContextMenu: function() {
50794                 if (!this.contextMenu) {
50795 //                      Create the View's context menu
50796                         this.contextMenu = new Roo.menu.Menu({
50797                                 id: this.id + "-contextmenu"
50798                         });
50799                         this.el.on("contextmenu", this.showContextMenu, this);
50800                 }
50801                 return this.contextMenu;
50802         },
50803         
50804         disableContextMenu: function() {
50805                 if (this.contextMenu) {
50806                         this.el.un("contextmenu", this.showContextMenu, this);
50807                 }
50808         },
50809
50810         showContextMenu: function(e, item) {
50811         item = this.findItemFromChild(e.getTarget());
50812                 if (item) {
50813                         e.stopEvent();
50814                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50815                         this.contextMenu.showAt(e.getXY());
50816             }
50817     },
50818
50819 /**
50820  *      Remove {@link Roo.data.Record}s at the specified indices.
50821  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50822  */
50823     remove: function(selectedIndices) {
50824                 selectedIndices = [].concat(selectedIndices);
50825                 for (var i = 0; i < selectedIndices.length; i++) {
50826                         var rec = this.store.getAt(selectedIndices[i]);
50827                         this.store.remove(rec);
50828                 }
50829     },
50830
50831 /**
50832  *      Double click fires the event, but also, if this is draggable, and there is only one other
50833  *      related DropZone, it transfers the selected node.
50834  */
50835     onDblClick : function(e){
50836         var item = this.findItemFromChild(e.getTarget());
50837         if(item){
50838             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50839                 return false;
50840             }
50841             if (this.dragGroup) {
50842                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50843                     while (targets.indexOf(this.dropZone) > -1) {
50844                             targets.remove(this.dropZone);
50845                                 }
50846                     if (targets.length == 1) {
50847                                         this.dragZone.cachedTarget = null;
50848                         var el = Roo.get(targets[0].getEl());
50849                         var box = el.getBox(true);
50850                         targets[0].onNodeDrop(el.dom, {
50851                                 target: el.dom,
50852                                 xy: [box.x, box.y + box.height - 1]
50853                         }, null, this.getDragData(e));
50854                     }
50855                 }
50856         }
50857     },
50858     
50859     handleSelection: function(e) {
50860                 this.dragZone.cachedTarget = null;
50861         var item = this.findItemFromChild(e.getTarget());
50862         if (!item) {
50863                 this.clearSelections(true);
50864                 return;
50865         }
50866                 if (item && (this.multiSelect || this.singleSelect)){
50867                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50868                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50869                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50870                                 this.unselect(item);
50871                         } else {
50872                                 this.select(item, this.multiSelect && e.ctrlKey);
50873                                 this.lastSelection = item;
50874                         }
50875                 }
50876     },
50877
50878     onItemClick : function(item, index, e){
50879                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50880                         return false;
50881                 }
50882                 return true;
50883     },
50884
50885     unselect : function(nodeInfo, suppressEvent){
50886                 var node = this.getNode(nodeInfo);
50887                 if(node && this.isSelected(node)){
50888                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50889                                 Roo.fly(node).removeClass(this.selectedClass);
50890                                 this.selections.remove(node);
50891                                 if(!suppressEvent){
50892                                         this.fireEvent("selectionchange", this, this.selections);
50893                                 }
50894                         }
50895                 }
50896     }
50897 });
50898 /*
50899  * Based on:
50900  * Ext JS Library 1.1.1
50901  * Copyright(c) 2006-2007, Ext JS, LLC.
50902  *
50903  * Originally Released Under LGPL - original licence link has changed is not relivant.
50904  *
50905  * Fork - LGPL
50906  * <script type="text/javascript">
50907  */
50908  
50909 /**
50910  * @class Roo.LayoutManager
50911  * @extends Roo.util.Observable
50912  * Base class for layout managers.
50913  */
50914 Roo.LayoutManager = function(container, config){
50915     Roo.LayoutManager.superclass.constructor.call(this);
50916     this.el = Roo.get(container);
50917     // ie scrollbar fix
50918     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50919         document.body.scroll = "no";
50920     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50921         this.el.position('relative');
50922     }
50923     this.id = this.el.id;
50924     this.el.addClass("x-layout-container");
50925     /** false to disable window resize monitoring @type Boolean */
50926     this.monitorWindowResize = true;
50927     this.regions = {};
50928     this.addEvents({
50929         /**
50930          * @event layout
50931          * Fires when a layout is performed. 
50932          * @param {Roo.LayoutManager} this
50933          */
50934         "layout" : true,
50935         /**
50936          * @event regionresized
50937          * Fires when the user resizes a region. 
50938          * @param {Roo.LayoutRegion} region The resized region
50939          * @param {Number} newSize The new size (width for east/west, height for north/south)
50940          */
50941         "regionresized" : true,
50942         /**
50943          * @event regioncollapsed
50944          * Fires when a region is collapsed. 
50945          * @param {Roo.LayoutRegion} region The collapsed region
50946          */
50947         "regioncollapsed" : true,
50948         /**
50949          * @event regionexpanded
50950          * Fires when a region is expanded.  
50951          * @param {Roo.LayoutRegion} region The expanded region
50952          */
50953         "regionexpanded" : true
50954     });
50955     this.updating = false;
50956     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50957 };
50958
50959 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50960     /**
50961      * Returns true if this layout is currently being updated
50962      * @return {Boolean}
50963      */
50964     isUpdating : function(){
50965         return this.updating; 
50966     },
50967     
50968     /**
50969      * Suspend the LayoutManager from doing auto-layouts while
50970      * making multiple add or remove calls
50971      */
50972     beginUpdate : function(){
50973         this.updating = true;    
50974     },
50975     
50976     /**
50977      * Restore auto-layouts and optionally disable the manager from performing a layout
50978      * @param {Boolean} noLayout true to disable a layout update 
50979      */
50980     endUpdate : function(noLayout){
50981         this.updating = false;
50982         if(!noLayout){
50983             this.layout();
50984         }    
50985     },
50986     
50987     layout: function(){
50988         
50989     },
50990     
50991     onRegionResized : function(region, newSize){
50992         this.fireEvent("regionresized", region, newSize);
50993         this.layout();
50994     },
50995     
50996     onRegionCollapsed : function(region){
50997         this.fireEvent("regioncollapsed", region);
50998     },
50999     
51000     onRegionExpanded : function(region){
51001         this.fireEvent("regionexpanded", region);
51002     },
51003         
51004     /**
51005      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51006      * performs box-model adjustments.
51007      * @return {Object} The size as an object {width: (the width), height: (the height)}
51008      */
51009     getViewSize : function(){
51010         var size;
51011         if(this.el.dom != document.body){
51012             size = this.el.getSize();
51013         }else{
51014             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51015         }
51016         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51017         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51018         return size;
51019     },
51020     
51021     /**
51022      * Returns the Element this layout is bound to.
51023      * @return {Roo.Element}
51024      */
51025     getEl : function(){
51026         return this.el;
51027     },
51028     
51029     /**
51030      * Returns the specified region.
51031      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51032      * @return {Roo.LayoutRegion}
51033      */
51034     getRegion : function(target){
51035         return this.regions[target.toLowerCase()];
51036     },
51037     
51038     onWindowResize : function(){
51039         if(this.monitorWindowResize){
51040             this.layout();
51041         }
51042     }
51043 });/*
51044  * Based on:
51045  * Ext JS Library 1.1.1
51046  * Copyright(c) 2006-2007, Ext JS, LLC.
51047  *
51048  * Originally Released Under LGPL - original licence link has changed is not relivant.
51049  *
51050  * Fork - LGPL
51051  * <script type="text/javascript">
51052  */
51053 /**
51054  * @class Roo.BorderLayout
51055  * @extends Roo.LayoutManager
51056  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51057  * please see: <br><br>
51058  * <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>
51059  * <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>
51060  * Example:
51061  <pre><code>
51062  var layout = new Roo.BorderLayout(document.body, {
51063     north: {
51064         initialSize: 25,
51065         titlebar: false
51066     },
51067     west: {
51068         split:true,
51069         initialSize: 200,
51070         minSize: 175,
51071         maxSize: 400,
51072         titlebar: true,
51073         collapsible: true
51074     },
51075     east: {
51076         split:true,
51077         initialSize: 202,
51078         minSize: 175,
51079         maxSize: 400,
51080         titlebar: true,
51081         collapsible: true
51082     },
51083     south: {
51084         split:true,
51085         initialSize: 100,
51086         minSize: 100,
51087         maxSize: 200,
51088         titlebar: true,
51089         collapsible: true
51090     },
51091     center: {
51092         titlebar: true,
51093         autoScroll:true,
51094         resizeTabs: true,
51095         minTabWidth: 50,
51096         preferredTabWidth: 150
51097     }
51098 });
51099
51100 // shorthand
51101 var CP = Roo.ContentPanel;
51102
51103 layout.beginUpdate();
51104 layout.add("north", new CP("north", "North"));
51105 layout.add("south", new CP("south", {title: "South", closable: true}));
51106 layout.add("west", new CP("west", {title: "West"}));
51107 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51108 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51109 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51110 layout.getRegion("center").showPanel("center1");
51111 layout.endUpdate();
51112 </code></pre>
51113
51114 <b>The container the layout is rendered into can be either the body element or any other element.
51115 If it is not the body element, the container needs to either be an absolute positioned element,
51116 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51117 the container size if it is not the body element.</b>
51118
51119 * @constructor
51120 * Create a new BorderLayout
51121 * @param {String/HTMLElement/Element} container The container this layout is bound to
51122 * @param {Object} config Configuration options
51123  */
51124 Roo.BorderLayout = function(container, config){
51125     config = config || {};
51126     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51127     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51128     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51129         var target = this.factory.validRegions[i];
51130         if(config[target]){
51131             this.addRegion(target, config[target]);
51132         }
51133     }
51134 };
51135
51136 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51137     /**
51138      * Creates and adds a new region if it doesn't already exist.
51139      * @param {String} target The target region key (north, south, east, west or center).
51140      * @param {Object} config The regions config object
51141      * @return {BorderLayoutRegion} The new region
51142      */
51143     addRegion : function(target, config){
51144         if(!this.regions[target]){
51145             var r = this.factory.create(target, this, config);
51146             this.bindRegion(target, r);
51147         }
51148         return this.regions[target];
51149     },
51150
51151     // private (kinda)
51152     bindRegion : function(name, r){
51153         this.regions[name] = r;
51154         r.on("visibilitychange", this.layout, this);
51155         r.on("paneladded", this.layout, this);
51156         r.on("panelremoved", this.layout, this);
51157         r.on("invalidated", this.layout, this);
51158         r.on("resized", this.onRegionResized, this);
51159         r.on("collapsed", this.onRegionCollapsed, this);
51160         r.on("expanded", this.onRegionExpanded, this);
51161     },
51162
51163     /**
51164      * Performs a layout update.
51165      */
51166     layout : function(){
51167         if(this.updating) {
51168             return;
51169         }
51170         var size = this.getViewSize();
51171         var w = size.width;
51172         var h = size.height;
51173         var centerW = w;
51174         var centerH = h;
51175         var centerY = 0;
51176         var centerX = 0;
51177         //var x = 0, y = 0;
51178
51179         var rs = this.regions;
51180         var north = rs["north"];
51181         var south = rs["south"]; 
51182         var west = rs["west"];
51183         var east = rs["east"];
51184         var center = rs["center"];
51185         //if(this.hideOnLayout){ // not supported anymore
51186             //c.el.setStyle("display", "none");
51187         //}
51188         if(north && north.isVisible()){
51189             var b = north.getBox();
51190             var m = north.getMargins();
51191             b.width = w - (m.left+m.right);
51192             b.x = m.left;
51193             b.y = m.top;
51194             centerY = b.height + b.y + m.bottom;
51195             centerH -= centerY;
51196             north.updateBox(this.safeBox(b));
51197         }
51198         if(south && south.isVisible()){
51199             var b = south.getBox();
51200             var m = south.getMargins();
51201             b.width = w - (m.left+m.right);
51202             b.x = m.left;
51203             var totalHeight = (b.height + m.top + m.bottom);
51204             b.y = h - totalHeight + m.top;
51205             centerH -= totalHeight;
51206             south.updateBox(this.safeBox(b));
51207         }
51208         if(west && west.isVisible()){
51209             var b = west.getBox();
51210             var m = west.getMargins();
51211             b.height = centerH - (m.top+m.bottom);
51212             b.x = m.left;
51213             b.y = centerY + m.top;
51214             var totalWidth = (b.width + m.left + m.right);
51215             centerX += totalWidth;
51216             centerW -= totalWidth;
51217             west.updateBox(this.safeBox(b));
51218         }
51219         if(east && east.isVisible()){
51220             var b = east.getBox();
51221             var m = east.getMargins();
51222             b.height = centerH - (m.top+m.bottom);
51223             var totalWidth = (b.width + m.left + m.right);
51224             b.x = w - totalWidth + m.left;
51225             b.y = centerY + m.top;
51226             centerW -= totalWidth;
51227             east.updateBox(this.safeBox(b));
51228         }
51229         if(center){
51230             var m = center.getMargins();
51231             var centerBox = {
51232                 x: centerX + m.left,
51233                 y: centerY + m.top,
51234                 width: centerW - (m.left+m.right),
51235                 height: centerH - (m.top+m.bottom)
51236             };
51237             //if(this.hideOnLayout){
51238                 //center.el.setStyle("display", "block");
51239             //}
51240             center.updateBox(this.safeBox(centerBox));
51241         }
51242         this.el.repaint();
51243         this.fireEvent("layout", this);
51244     },
51245
51246     // private
51247     safeBox : function(box){
51248         box.width = Math.max(0, box.width);
51249         box.height = Math.max(0, box.height);
51250         return box;
51251     },
51252
51253     /**
51254      * Adds a ContentPanel (or subclass) to this layout.
51255      * @param {String} target The target region key (north, south, east, west or center).
51256      * @param {Roo.ContentPanel} panel The panel to add
51257      * @return {Roo.ContentPanel} The added panel
51258      */
51259     add : function(target, panel){
51260          
51261         target = target.toLowerCase();
51262         return this.regions[target].add(panel);
51263     },
51264
51265     /**
51266      * Remove a ContentPanel (or subclass) to this layout.
51267      * @param {String} target The target region key (north, south, east, west or center).
51268      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51269      * @return {Roo.ContentPanel} The removed panel
51270      */
51271     remove : function(target, panel){
51272         target = target.toLowerCase();
51273         return this.regions[target].remove(panel);
51274     },
51275
51276     /**
51277      * Searches all regions for a panel with the specified id
51278      * @param {String} panelId
51279      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51280      */
51281     findPanel : function(panelId){
51282         var rs = this.regions;
51283         for(var target in rs){
51284             if(typeof rs[target] != "function"){
51285                 var p = rs[target].getPanel(panelId);
51286                 if(p){
51287                     return p;
51288                 }
51289             }
51290         }
51291         return null;
51292     },
51293
51294     /**
51295      * Searches all regions for a panel with the specified id and activates (shows) it.
51296      * @param {String/ContentPanel} panelId The panels id or the panel itself
51297      * @return {Roo.ContentPanel} The shown panel or null
51298      */
51299     showPanel : function(panelId) {
51300       var rs = this.regions;
51301       for(var target in rs){
51302          var r = rs[target];
51303          if(typeof r != "function"){
51304             if(r.hasPanel(panelId)){
51305                return r.showPanel(panelId);
51306             }
51307          }
51308       }
51309       return null;
51310    },
51311
51312    /**
51313      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51314      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51315      */
51316     restoreState : function(provider){
51317         if(!provider){
51318             provider = Roo.state.Manager;
51319         }
51320         var sm = new Roo.LayoutStateManager();
51321         sm.init(this, provider);
51322     },
51323
51324     /**
51325      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51326      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51327      * a valid ContentPanel config object.  Example:
51328      * <pre><code>
51329 // Create the main layout
51330 var layout = new Roo.BorderLayout('main-ct', {
51331     west: {
51332         split:true,
51333         minSize: 175,
51334         titlebar: true
51335     },
51336     center: {
51337         title:'Components'
51338     }
51339 }, 'main-ct');
51340
51341 // Create and add multiple ContentPanels at once via configs
51342 layout.batchAdd({
51343    west: {
51344        id: 'source-files',
51345        autoCreate:true,
51346        title:'Ext Source Files',
51347        autoScroll:true,
51348        fitToFrame:true
51349    },
51350    center : {
51351        el: cview,
51352        autoScroll:true,
51353        fitToFrame:true,
51354        toolbar: tb,
51355        resizeEl:'cbody'
51356    }
51357 });
51358 </code></pre>
51359      * @param {Object} regions An object containing ContentPanel configs by region name
51360      */
51361     batchAdd : function(regions){
51362         this.beginUpdate();
51363         for(var rname in regions){
51364             var lr = this.regions[rname];
51365             if(lr){
51366                 this.addTypedPanels(lr, regions[rname]);
51367             }
51368         }
51369         this.endUpdate();
51370     },
51371
51372     // private
51373     addTypedPanels : function(lr, ps){
51374         if(typeof ps == 'string'){
51375             lr.add(new Roo.ContentPanel(ps));
51376         }
51377         else if(ps instanceof Array){
51378             for(var i =0, len = ps.length; i < len; i++){
51379                 this.addTypedPanels(lr, ps[i]);
51380             }
51381         }
51382         else if(!ps.events){ // raw config?
51383             var el = ps.el;
51384             delete ps.el; // prevent conflict
51385             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51386         }
51387         else {  // panel object assumed!
51388             lr.add(ps);
51389         }
51390     },
51391     /**
51392      * Adds a xtype elements to the layout.
51393      * <pre><code>
51394
51395 layout.addxtype({
51396        xtype : 'ContentPanel',
51397        region: 'west',
51398        items: [ .... ]
51399    }
51400 );
51401
51402 layout.addxtype({
51403         xtype : 'NestedLayoutPanel',
51404         region: 'west',
51405         layout: {
51406            center: { },
51407            west: { }   
51408         },
51409         items : [ ... list of content panels or nested layout panels.. ]
51410    }
51411 );
51412 </code></pre>
51413      * @param {Object} cfg Xtype definition of item to add.
51414      */
51415     addxtype : function(cfg)
51416     {
51417         // basically accepts a pannel...
51418         // can accept a layout region..!?!?
51419         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51420         
51421         if (!cfg.xtype.match(/Panel$/)) {
51422             return false;
51423         }
51424         var ret = false;
51425         
51426         if (typeof(cfg.region) == 'undefined') {
51427             Roo.log("Failed to add Panel, region was not set");
51428             Roo.log(cfg);
51429             return false;
51430         }
51431         var region = cfg.region;
51432         delete cfg.region;
51433         
51434           
51435         var xitems = [];
51436         if (cfg.items) {
51437             xitems = cfg.items;
51438             delete cfg.items;
51439         }
51440         var nb = false;
51441         
51442         switch(cfg.xtype) 
51443         {
51444             case 'ContentPanel':  // ContentPanel (el, cfg)
51445             case 'ScrollPanel':  // ContentPanel (el, cfg)
51446             case 'ViewPanel': 
51447                 if(cfg.autoCreate) {
51448                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51449                 } else {
51450                     var el = this.el.createChild();
51451                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51452                 }
51453                 
51454                 this.add(region, ret);
51455                 break;
51456             
51457             
51458             case 'TreePanel': // our new panel!
51459                 cfg.el = this.el.createChild();
51460                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51461                 this.add(region, ret);
51462                 break;
51463             
51464             case 'NestedLayoutPanel': 
51465                 // create a new Layout (which is  a Border Layout...
51466                 var el = this.el.createChild();
51467                 var clayout = cfg.layout;
51468                 delete cfg.layout;
51469                 clayout.items   = clayout.items  || [];
51470                 // replace this exitems with the clayout ones..
51471                 xitems = clayout.items;
51472                  
51473                 
51474                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51475                     cfg.background = false;
51476                 }
51477                 var layout = new Roo.BorderLayout(el, clayout);
51478                 
51479                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51480                 //console.log('adding nested layout panel '  + cfg.toSource());
51481                 this.add(region, ret);
51482                 nb = {}; /// find first...
51483                 break;
51484                 
51485             case 'GridPanel': 
51486             
51487                 // needs grid and region
51488                 
51489                 //var el = this.getRegion(region).el.createChild();
51490                 var el = this.el.createChild();
51491                 // create the grid first...
51492                 
51493                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51494                 delete cfg.grid;
51495                 if (region == 'center' && this.active ) {
51496                     cfg.background = false;
51497                 }
51498                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51499                 
51500                 this.add(region, ret);
51501                 if (cfg.background) {
51502                     ret.on('activate', function(gp) {
51503                         if (!gp.grid.rendered) {
51504                             gp.grid.render();
51505                         }
51506                     });
51507                 } else {
51508                     grid.render();
51509                 }
51510                 break;
51511            
51512            
51513            
51514                 
51515                 
51516                 
51517             default:
51518                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51519                     
51520                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51521                     this.add(region, ret);
51522                 } else {
51523                 
51524                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51525                     return null;
51526                 }
51527                 
51528              // GridPanel (grid, cfg)
51529             
51530         }
51531         this.beginUpdate();
51532         // add children..
51533         var region = '';
51534         var abn = {};
51535         Roo.each(xitems, function(i)  {
51536             region = nb && i.region ? i.region : false;
51537             
51538             var add = ret.addxtype(i);
51539            
51540             if (region) {
51541                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51542                 if (!i.background) {
51543                     abn[region] = nb[region] ;
51544                 }
51545             }
51546             
51547         });
51548         this.endUpdate();
51549
51550         // make the last non-background panel active..
51551         //if (nb) { Roo.log(abn); }
51552         if (nb) {
51553             
51554             for(var r in abn) {
51555                 region = this.getRegion(r);
51556                 if (region) {
51557                     // tried using nb[r], but it does not work..
51558                      
51559                     region.showPanel(abn[r]);
51560                    
51561                 }
51562             }
51563         }
51564         return ret;
51565         
51566     }
51567 });
51568
51569 /**
51570  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51571  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51572  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51573  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51574  * <pre><code>
51575 // shorthand
51576 var CP = Roo.ContentPanel;
51577
51578 var layout = Roo.BorderLayout.create({
51579     north: {
51580         initialSize: 25,
51581         titlebar: false,
51582         panels: [new CP("north", "North")]
51583     },
51584     west: {
51585         split:true,
51586         initialSize: 200,
51587         minSize: 175,
51588         maxSize: 400,
51589         titlebar: true,
51590         collapsible: true,
51591         panels: [new CP("west", {title: "West"})]
51592     },
51593     east: {
51594         split:true,
51595         initialSize: 202,
51596         minSize: 175,
51597         maxSize: 400,
51598         titlebar: true,
51599         collapsible: true,
51600         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51601     },
51602     south: {
51603         split:true,
51604         initialSize: 100,
51605         minSize: 100,
51606         maxSize: 200,
51607         titlebar: true,
51608         collapsible: true,
51609         panels: [new CP("south", {title: "South", closable: true})]
51610     },
51611     center: {
51612         titlebar: true,
51613         autoScroll:true,
51614         resizeTabs: true,
51615         minTabWidth: 50,
51616         preferredTabWidth: 150,
51617         panels: [
51618             new CP("center1", {title: "Close Me", closable: true}),
51619             new CP("center2", {title: "Center Panel", closable: false})
51620         ]
51621     }
51622 }, document.body);
51623
51624 layout.getRegion("center").showPanel("center1");
51625 </code></pre>
51626  * @param config
51627  * @param targetEl
51628  */
51629 Roo.BorderLayout.create = function(config, targetEl){
51630     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51631     layout.beginUpdate();
51632     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51633     for(var j = 0, jlen = regions.length; j < jlen; j++){
51634         var lr = regions[j];
51635         if(layout.regions[lr] && config[lr].panels){
51636             var r = layout.regions[lr];
51637             var ps = config[lr].panels;
51638             layout.addTypedPanels(r, ps);
51639         }
51640     }
51641     layout.endUpdate();
51642     return layout;
51643 };
51644
51645 // private
51646 Roo.BorderLayout.RegionFactory = {
51647     // private
51648     validRegions : ["north","south","east","west","center"],
51649
51650     // private
51651     create : function(target, mgr, config){
51652         target = target.toLowerCase();
51653         if(config.lightweight || config.basic){
51654             return new Roo.BasicLayoutRegion(mgr, config, target);
51655         }
51656         switch(target){
51657             case "north":
51658                 return new Roo.NorthLayoutRegion(mgr, config);
51659             case "south":
51660                 return new Roo.SouthLayoutRegion(mgr, config);
51661             case "east":
51662                 return new Roo.EastLayoutRegion(mgr, config);
51663             case "west":
51664                 return new Roo.WestLayoutRegion(mgr, config);
51665             case "center":
51666                 return new Roo.CenterLayoutRegion(mgr, config);
51667         }
51668         throw 'Layout region "'+target+'" not supported.';
51669     }
51670 };/*
51671  * Based on:
51672  * Ext JS Library 1.1.1
51673  * Copyright(c) 2006-2007, Ext JS, LLC.
51674  *
51675  * Originally Released Under LGPL - original licence link has changed is not relivant.
51676  *
51677  * Fork - LGPL
51678  * <script type="text/javascript">
51679  */
51680  
51681 /**
51682  * @class Roo.BasicLayoutRegion
51683  * @extends Roo.util.Observable
51684  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51685  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51686  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51687  */
51688 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51689     this.mgr = mgr;
51690     this.position  = pos;
51691     this.events = {
51692         /**
51693          * @scope Roo.BasicLayoutRegion
51694          */
51695         
51696         /**
51697          * @event beforeremove
51698          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51699          * @param {Roo.LayoutRegion} this
51700          * @param {Roo.ContentPanel} panel The panel
51701          * @param {Object} e The cancel event object
51702          */
51703         "beforeremove" : true,
51704         /**
51705          * @event invalidated
51706          * Fires when the layout for this region is changed.
51707          * @param {Roo.LayoutRegion} this
51708          */
51709         "invalidated" : true,
51710         /**
51711          * @event visibilitychange
51712          * Fires when this region is shown or hidden 
51713          * @param {Roo.LayoutRegion} this
51714          * @param {Boolean} visibility true or false
51715          */
51716         "visibilitychange" : true,
51717         /**
51718          * @event paneladded
51719          * Fires when a panel is added. 
51720          * @param {Roo.LayoutRegion} this
51721          * @param {Roo.ContentPanel} panel The panel
51722          */
51723         "paneladded" : true,
51724         /**
51725          * @event panelremoved
51726          * Fires when a panel is removed. 
51727          * @param {Roo.LayoutRegion} this
51728          * @param {Roo.ContentPanel} panel The panel
51729          */
51730         "panelremoved" : true,
51731         /**
51732          * @event beforecollapse
51733          * Fires when this region before collapse.
51734          * @param {Roo.LayoutRegion} this
51735          */
51736         "beforecollapse" : true,
51737         /**
51738          * @event collapsed
51739          * Fires when this region is collapsed.
51740          * @param {Roo.LayoutRegion} this
51741          */
51742         "collapsed" : true,
51743         /**
51744          * @event expanded
51745          * Fires when this region is expanded.
51746          * @param {Roo.LayoutRegion} this
51747          */
51748         "expanded" : true,
51749         /**
51750          * @event slideshow
51751          * Fires when this region is slid into view.
51752          * @param {Roo.LayoutRegion} this
51753          */
51754         "slideshow" : true,
51755         /**
51756          * @event slidehide
51757          * Fires when this region slides out of view. 
51758          * @param {Roo.LayoutRegion} this
51759          */
51760         "slidehide" : true,
51761         /**
51762          * @event panelactivated
51763          * Fires when a panel is activated. 
51764          * @param {Roo.LayoutRegion} this
51765          * @param {Roo.ContentPanel} panel The activated panel
51766          */
51767         "panelactivated" : true,
51768         /**
51769          * @event resized
51770          * Fires when the user resizes this region. 
51771          * @param {Roo.LayoutRegion} this
51772          * @param {Number} newSize The new size (width for east/west, height for north/south)
51773          */
51774         "resized" : true
51775     };
51776     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51777     this.panels = new Roo.util.MixedCollection();
51778     this.panels.getKey = this.getPanelId.createDelegate(this);
51779     this.box = null;
51780     this.activePanel = null;
51781     // ensure listeners are added...
51782     
51783     if (config.listeners || config.events) {
51784         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51785             listeners : config.listeners || {},
51786             events : config.events || {}
51787         });
51788     }
51789     
51790     if(skipConfig !== true){
51791         this.applyConfig(config);
51792     }
51793 };
51794
51795 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51796     getPanelId : function(p){
51797         return p.getId();
51798     },
51799     
51800     applyConfig : function(config){
51801         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51802         this.config = config;
51803         
51804     },
51805     
51806     /**
51807      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51808      * the width, for horizontal (north, south) the height.
51809      * @param {Number} newSize The new width or height
51810      */
51811     resizeTo : function(newSize){
51812         var el = this.el ? this.el :
51813                  (this.activePanel ? this.activePanel.getEl() : null);
51814         if(el){
51815             switch(this.position){
51816                 case "east":
51817                 case "west":
51818                     el.setWidth(newSize);
51819                     this.fireEvent("resized", this, newSize);
51820                 break;
51821                 case "north":
51822                 case "south":
51823                     el.setHeight(newSize);
51824                     this.fireEvent("resized", this, newSize);
51825                 break;                
51826             }
51827         }
51828     },
51829     
51830     getBox : function(){
51831         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51832     },
51833     
51834     getMargins : function(){
51835         return this.margins;
51836     },
51837     
51838     updateBox : function(box){
51839         this.box = box;
51840         var el = this.activePanel.getEl();
51841         el.dom.style.left = box.x + "px";
51842         el.dom.style.top = box.y + "px";
51843         this.activePanel.setSize(box.width, box.height);
51844     },
51845     
51846     /**
51847      * Returns the container element for this region.
51848      * @return {Roo.Element}
51849      */
51850     getEl : function(){
51851         return this.activePanel;
51852     },
51853     
51854     /**
51855      * Returns true if this region is currently visible.
51856      * @return {Boolean}
51857      */
51858     isVisible : function(){
51859         return this.activePanel ? true : false;
51860     },
51861     
51862     setActivePanel : function(panel){
51863         panel = this.getPanel(panel);
51864         if(this.activePanel && this.activePanel != panel){
51865             this.activePanel.setActiveState(false);
51866             this.activePanel.getEl().setLeftTop(-10000,-10000);
51867         }
51868         this.activePanel = panel;
51869         panel.setActiveState(true);
51870         if(this.box){
51871             panel.setSize(this.box.width, this.box.height);
51872         }
51873         this.fireEvent("panelactivated", this, panel);
51874         this.fireEvent("invalidated");
51875     },
51876     
51877     /**
51878      * Show the specified panel.
51879      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51880      * @return {Roo.ContentPanel} The shown panel or null
51881      */
51882     showPanel : function(panel){
51883         if(panel = this.getPanel(panel)){
51884             this.setActivePanel(panel);
51885         }
51886         return panel;
51887     },
51888     
51889     /**
51890      * Get the active panel for this region.
51891      * @return {Roo.ContentPanel} The active panel or null
51892      */
51893     getActivePanel : function(){
51894         return this.activePanel;
51895     },
51896     
51897     /**
51898      * Add the passed ContentPanel(s)
51899      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51900      * @return {Roo.ContentPanel} The panel added (if only one was added)
51901      */
51902     add : function(panel){
51903         if(arguments.length > 1){
51904             for(var i = 0, len = arguments.length; i < len; i++) {
51905                 this.add(arguments[i]);
51906             }
51907             return null;
51908         }
51909         if(this.hasPanel(panel)){
51910             this.showPanel(panel);
51911             return panel;
51912         }
51913         var el = panel.getEl();
51914         if(el.dom.parentNode != this.mgr.el.dom){
51915             this.mgr.el.dom.appendChild(el.dom);
51916         }
51917         if(panel.setRegion){
51918             panel.setRegion(this);
51919         }
51920         this.panels.add(panel);
51921         el.setStyle("position", "absolute");
51922         if(!panel.background){
51923             this.setActivePanel(panel);
51924             if(this.config.initialSize && this.panels.getCount()==1){
51925                 this.resizeTo(this.config.initialSize);
51926             }
51927         }
51928         this.fireEvent("paneladded", this, panel);
51929         return panel;
51930     },
51931     
51932     /**
51933      * Returns true if the panel is in this region.
51934      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51935      * @return {Boolean}
51936      */
51937     hasPanel : function(panel){
51938         if(typeof panel == "object"){ // must be panel obj
51939             panel = panel.getId();
51940         }
51941         return this.getPanel(panel) ? true : false;
51942     },
51943     
51944     /**
51945      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51946      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51947      * @param {Boolean} preservePanel Overrides the config preservePanel option
51948      * @return {Roo.ContentPanel} The panel that was removed
51949      */
51950     remove : function(panel, preservePanel){
51951         panel = this.getPanel(panel);
51952         if(!panel){
51953             return null;
51954         }
51955         var e = {};
51956         this.fireEvent("beforeremove", this, panel, e);
51957         if(e.cancel === true){
51958             return null;
51959         }
51960         var panelId = panel.getId();
51961         this.panels.removeKey(panelId);
51962         return panel;
51963     },
51964     
51965     /**
51966      * Returns the panel specified or null if it's not in this region.
51967      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51968      * @return {Roo.ContentPanel}
51969      */
51970     getPanel : function(id){
51971         if(typeof id == "object"){ // must be panel obj
51972             return id;
51973         }
51974         return this.panels.get(id);
51975     },
51976     
51977     /**
51978      * Returns this regions position (north/south/east/west/center).
51979      * @return {String} 
51980      */
51981     getPosition: function(){
51982         return this.position;    
51983     }
51984 });/*
51985  * Based on:
51986  * Ext JS Library 1.1.1
51987  * Copyright(c) 2006-2007, Ext JS, LLC.
51988  *
51989  * Originally Released Under LGPL - original licence link has changed is not relivant.
51990  *
51991  * Fork - LGPL
51992  * <script type="text/javascript">
51993  */
51994  
51995 /**
51996  * @class Roo.LayoutRegion
51997  * @extends Roo.BasicLayoutRegion
51998  * This class represents a region in a layout manager.
51999  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52000  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52001  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52002  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52003  * @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})
52004  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52005  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52006  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52007  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52008  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52009  * @cfg {String}    title           The title for the region (overrides panel titles)
52010  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52011  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52012  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52013  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52014  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52015  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52016  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52017  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52018  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52019  * @cfg {Boolean}   showPin         True to show a pin button
52020  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52021  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52022  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52023  * @cfg {Number}    width           For East/West panels
52024  * @cfg {Number}    height          For North/South panels
52025  * @cfg {Boolean}   split           To show the splitter
52026  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52027  */
52028 Roo.LayoutRegion = function(mgr, config, pos){
52029     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52030     var dh = Roo.DomHelper;
52031     /** This region's container element 
52032     * @type Roo.Element */
52033     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52034     /** This region's title element 
52035     * @type Roo.Element */
52036
52037     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52038         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52039         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52040     ]}, true);
52041     this.titleEl.enableDisplayMode();
52042     /** This region's title text element 
52043     * @type HTMLElement */
52044     this.titleTextEl = this.titleEl.dom.firstChild;
52045     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52046     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52047     this.closeBtn.enableDisplayMode();
52048     this.closeBtn.on("click", this.closeClicked, this);
52049     this.closeBtn.hide();
52050
52051     this.createBody(config);
52052     this.visible = true;
52053     this.collapsed = false;
52054
52055     if(config.hideWhenEmpty){
52056         this.hide();
52057         this.on("paneladded", this.validateVisibility, this);
52058         this.on("panelremoved", this.validateVisibility, this);
52059     }
52060     this.applyConfig(config);
52061 };
52062
52063 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52064
52065     createBody : function(){
52066         /** This region's body element 
52067         * @type Roo.Element */
52068         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52069     },
52070
52071     applyConfig : function(c){
52072         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52073             var dh = Roo.DomHelper;
52074             if(c.titlebar !== false){
52075                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52076                 this.collapseBtn.on("click", this.collapse, this);
52077                 this.collapseBtn.enableDisplayMode();
52078
52079                 if(c.showPin === true || this.showPin){
52080                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52081                     this.stickBtn.enableDisplayMode();
52082                     this.stickBtn.on("click", this.expand, this);
52083                     this.stickBtn.hide();
52084                 }
52085             }
52086             /** This region's collapsed element
52087             * @type Roo.Element */
52088             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52089                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52090             ]}, true);
52091             if(c.floatable !== false){
52092                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52093                this.collapsedEl.on("click", this.collapseClick, this);
52094             }
52095
52096             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52097                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52098                    id: "message", unselectable: "on", style:{"float":"left"}});
52099                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52100              }
52101             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52102             this.expandBtn.on("click", this.expand, this);
52103         }
52104         if(this.collapseBtn){
52105             this.collapseBtn.setVisible(c.collapsible == true);
52106         }
52107         this.cmargins = c.cmargins || this.cmargins ||
52108                          (this.position == "west" || this.position == "east" ?
52109                              {top: 0, left: 2, right:2, bottom: 0} :
52110                              {top: 2, left: 0, right:0, bottom: 2});
52111         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52112         this.bottomTabs = c.tabPosition != "top";
52113         this.autoScroll = c.autoScroll || false;
52114         if(this.autoScroll){
52115             this.bodyEl.setStyle("overflow", "auto");
52116         }else{
52117             this.bodyEl.setStyle("overflow", "hidden");
52118         }
52119         //if(c.titlebar !== false){
52120             if((!c.titlebar && !c.title) || c.titlebar === false){
52121                 this.titleEl.hide();
52122             }else{
52123                 this.titleEl.show();
52124                 if(c.title){
52125                     this.titleTextEl.innerHTML = c.title;
52126                 }
52127             }
52128         //}
52129         this.duration = c.duration || .30;
52130         this.slideDuration = c.slideDuration || .45;
52131         this.config = c;
52132         if(c.collapsed){
52133             this.collapse(true);
52134         }
52135         if(c.hidden){
52136             this.hide();
52137         }
52138     },
52139     /**
52140      * Returns true if this region is currently visible.
52141      * @return {Boolean}
52142      */
52143     isVisible : function(){
52144         return this.visible;
52145     },
52146
52147     /**
52148      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52149      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52150      */
52151     setCollapsedTitle : function(title){
52152         title = title || "&#160;";
52153         if(this.collapsedTitleTextEl){
52154             this.collapsedTitleTextEl.innerHTML = title;
52155         }
52156     },
52157
52158     getBox : function(){
52159         var b;
52160         if(!this.collapsed){
52161             b = this.el.getBox(false, true);
52162         }else{
52163             b = this.collapsedEl.getBox(false, true);
52164         }
52165         return b;
52166     },
52167
52168     getMargins : function(){
52169         return this.collapsed ? this.cmargins : this.margins;
52170     },
52171
52172     highlight : function(){
52173         this.el.addClass("x-layout-panel-dragover");
52174     },
52175
52176     unhighlight : function(){
52177         this.el.removeClass("x-layout-panel-dragover");
52178     },
52179
52180     updateBox : function(box){
52181         this.box = box;
52182         if(!this.collapsed){
52183             this.el.dom.style.left = box.x + "px";
52184             this.el.dom.style.top = box.y + "px";
52185             this.updateBody(box.width, box.height);
52186         }else{
52187             this.collapsedEl.dom.style.left = box.x + "px";
52188             this.collapsedEl.dom.style.top = box.y + "px";
52189             this.collapsedEl.setSize(box.width, box.height);
52190         }
52191         if(this.tabs){
52192             this.tabs.autoSizeTabs();
52193         }
52194     },
52195
52196     updateBody : function(w, h){
52197         if(w !== null){
52198             this.el.setWidth(w);
52199             w -= this.el.getBorderWidth("rl");
52200             if(this.config.adjustments){
52201                 w += this.config.adjustments[0];
52202             }
52203         }
52204         if(h !== null){
52205             this.el.setHeight(h);
52206             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52207             h -= this.el.getBorderWidth("tb");
52208             if(this.config.adjustments){
52209                 h += this.config.adjustments[1];
52210             }
52211             this.bodyEl.setHeight(h);
52212             if(this.tabs){
52213                 h = this.tabs.syncHeight(h);
52214             }
52215         }
52216         if(this.panelSize){
52217             w = w !== null ? w : this.panelSize.width;
52218             h = h !== null ? h : this.panelSize.height;
52219         }
52220         if(this.activePanel){
52221             var el = this.activePanel.getEl();
52222             w = w !== null ? w : el.getWidth();
52223             h = h !== null ? h : el.getHeight();
52224             this.panelSize = {width: w, height: h};
52225             this.activePanel.setSize(w, h);
52226         }
52227         if(Roo.isIE && this.tabs){
52228             this.tabs.el.repaint();
52229         }
52230     },
52231
52232     /**
52233      * Returns the container element for this region.
52234      * @return {Roo.Element}
52235      */
52236     getEl : function(){
52237         return this.el;
52238     },
52239
52240     /**
52241      * Hides this region.
52242      */
52243     hide : function(){
52244         if(!this.collapsed){
52245             this.el.dom.style.left = "-2000px";
52246             this.el.hide();
52247         }else{
52248             this.collapsedEl.dom.style.left = "-2000px";
52249             this.collapsedEl.hide();
52250         }
52251         this.visible = false;
52252         this.fireEvent("visibilitychange", this, false);
52253     },
52254
52255     /**
52256      * Shows this region if it was previously hidden.
52257      */
52258     show : function(){
52259         if(!this.collapsed){
52260             this.el.show();
52261         }else{
52262             this.collapsedEl.show();
52263         }
52264         this.visible = true;
52265         this.fireEvent("visibilitychange", this, true);
52266     },
52267
52268     closeClicked : function(){
52269         if(this.activePanel){
52270             this.remove(this.activePanel);
52271         }
52272     },
52273
52274     collapseClick : function(e){
52275         if(this.isSlid){
52276            e.stopPropagation();
52277            this.slideIn();
52278         }else{
52279            e.stopPropagation();
52280            this.slideOut();
52281         }
52282     },
52283
52284     /**
52285      * Collapses this region.
52286      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52287      */
52288     collapse : function(skipAnim, skipCheck = false){
52289         if(this.collapsed) {
52290             return;
52291         }
52292         
52293         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52294             
52295             this.collapsed = true;
52296             if(this.split){
52297                 this.split.el.hide();
52298             }
52299             if(this.config.animate && skipAnim !== true){
52300                 this.fireEvent("invalidated", this);
52301                 this.animateCollapse();
52302             }else{
52303                 this.el.setLocation(-20000,-20000);
52304                 this.el.hide();
52305                 this.collapsedEl.show();
52306                 this.fireEvent("collapsed", this);
52307                 this.fireEvent("invalidated", this);
52308             }
52309         }
52310         
52311     },
52312
52313     animateCollapse : function(){
52314         // overridden
52315     },
52316
52317     /**
52318      * Expands this region if it was previously collapsed.
52319      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52320      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52321      */
52322     expand : function(e, skipAnim){
52323         if(e) {
52324             e.stopPropagation();
52325         }
52326         if(!this.collapsed || this.el.hasActiveFx()) {
52327             return;
52328         }
52329         if(this.isSlid){
52330             this.afterSlideIn();
52331             skipAnim = true;
52332         }
52333         this.collapsed = false;
52334         if(this.config.animate && skipAnim !== true){
52335             this.animateExpand();
52336         }else{
52337             this.el.show();
52338             if(this.split){
52339                 this.split.el.show();
52340             }
52341             this.collapsedEl.setLocation(-2000,-2000);
52342             this.collapsedEl.hide();
52343             this.fireEvent("invalidated", this);
52344             this.fireEvent("expanded", this);
52345         }
52346     },
52347
52348     animateExpand : function(){
52349         // overridden
52350     },
52351
52352     initTabs : function()
52353     {
52354         this.bodyEl.setStyle("overflow", "hidden");
52355         var ts = new Roo.TabPanel(
52356                 this.bodyEl.dom,
52357                 {
52358                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52359                     disableTooltips: this.config.disableTabTips,
52360                     toolbar : this.config.toolbar
52361                 }
52362         );
52363         if(this.config.hideTabs){
52364             ts.stripWrap.setDisplayed(false);
52365         }
52366         this.tabs = ts;
52367         ts.resizeTabs = this.config.resizeTabs === true;
52368         ts.minTabWidth = this.config.minTabWidth || 40;
52369         ts.maxTabWidth = this.config.maxTabWidth || 250;
52370         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52371         ts.monitorResize = false;
52372         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52373         ts.bodyEl.addClass('x-layout-tabs-body');
52374         this.panels.each(this.initPanelAsTab, this);
52375     },
52376
52377     initPanelAsTab : function(panel){
52378         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52379                     this.config.closeOnTab && panel.isClosable());
52380         if(panel.tabTip !== undefined){
52381             ti.setTooltip(panel.tabTip);
52382         }
52383         ti.on("activate", function(){
52384               this.setActivePanel(panel);
52385         }, this);
52386         if(this.config.closeOnTab){
52387             ti.on("beforeclose", function(t, e){
52388                 e.cancel = true;
52389                 this.remove(panel);
52390             }, this);
52391         }
52392         return ti;
52393     },
52394
52395     updatePanelTitle : function(panel, title){
52396         if(this.activePanel == panel){
52397             this.updateTitle(title);
52398         }
52399         if(this.tabs){
52400             var ti = this.tabs.getTab(panel.getEl().id);
52401             ti.setText(title);
52402             if(panel.tabTip !== undefined){
52403                 ti.setTooltip(panel.tabTip);
52404             }
52405         }
52406     },
52407
52408     updateTitle : function(title){
52409         if(this.titleTextEl && !this.config.title){
52410             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52411         }
52412     },
52413
52414     setActivePanel : function(panel){
52415         panel = this.getPanel(panel);
52416         if(this.activePanel && this.activePanel != panel){
52417             this.activePanel.setActiveState(false);
52418         }
52419         this.activePanel = panel;
52420         panel.setActiveState(true);
52421         if(this.panelSize){
52422             panel.setSize(this.panelSize.width, this.panelSize.height);
52423         }
52424         if(this.closeBtn){
52425             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52426         }
52427         this.updateTitle(panel.getTitle());
52428         if(this.tabs){
52429             this.fireEvent("invalidated", this);
52430         }
52431         this.fireEvent("panelactivated", this, panel);
52432     },
52433
52434     /**
52435      * Shows the specified panel.
52436      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52437      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52438      */
52439     showPanel : function(panel)
52440     {
52441         panel = this.getPanel(panel);
52442         if(panel){
52443             if(this.tabs){
52444                 var tab = this.tabs.getTab(panel.getEl().id);
52445                 if(tab.isHidden()){
52446                     this.tabs.unhideTab(tab.id);
52447                 }
52448                 tab.activate();
52449             }else{
52450                 this.setActivePanel(panel);
52451             }
52452         }
52453         return panel;
52454     },
52455
52456     /**
52457      * Get the active panel for this region.
52458      * @return {Roo.ContentPanel} The active panel or null
52459      */
52460     getActivePanel : function(){
52461         return this.activePanel;
52462     },
52463
52464     validateVisibility : function(){
52465         if(this.panels.getCount() < 1){
52466             this.updateTitle("&#160;");
52467             this.closeBtn.hide();
52468             this.hide();
52469         }else{
52470             if(!this.isVisible()){
52471                 this.show();
52472             }
52473         }
52474     },
52475
52476     /**
52477      * Adds the passed ContentPanel(s) to this region.
52478      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52479      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52480      */
52481     add : function(panel){
52482         if(arguments.length > 1){
52483             for(var i = 0, len = arguments.length; i < len; i++) {
52484                 this.add(arguments[i]);
52485             }
52486             return null;
52487         }
52488         if(this.hasPanel(panel)){
52489             this.showPanel(panel);
52490             return panel;
52491         }
52492         panel.setRegion(this);
52493         this.panels.add(panel);
52494         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52495             this.bodyEl.dom.appendChild(panel.getEl().dom);
52496             if(panel.background !== true){
52497                 this.setActivePanel(panel);
52498             }
52499             this.fireEvent("paneladded", this, panel);
52500             return panel;
52501         }
52502         if(!this.tabs){
52503             this.initTabs();
52504         }else{
52505             this.initPanelAsTab(panel);
52506         }
52507         if(panel.background !== true){
52508             this.tabs.activate(panel.getEl().id);
52509         }
52510         this.fireEvent("paneladded", this, panel);
52511         return panel;
52512     },
52513
52514     /**
52515      * Hides the tab for the specified panel.
52516      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52517      */
52518     hidePanel : function(panel){
52519         if(this.tabs && (panel = this.getPanel(panel))){
52520             this.tabs.hideTab(panel.getEl().id);
52521         }
52522     },
52523
52524     /**
52525      * Unhides the tab for a previously hidden panel.
52526      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52527      */
52528     unhidePanel : function(panel){
52529         if(this.tabs && (panel = this.getPanel(panel))){
52530             this.tabs.unhideTab(panel.getEl().id);
52531         }
52532     },
52533
52534     clearPanels : function(){
52535         while(this.panels.getCount() > 0){
52536              this.remove(this.panels.first());
52537         }
52538     },
52539
52540     /**
52541      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52542      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52543      * @param {Boolean} preservePanel Overrides the config preservePanel option
52544      * @return {Roo.ContentPanel} The panel that was removed
52545      */
52546     remove : function(panel, preservePanel){
52547         panel = this.getPanel(panel);
52548         if(!panel){
52549             return null;
52550         }
52551         var e = {};
52552         this.fireEvent("beforeremove", this, panel, e);
52553         if(e.cancel === true){
52554             return null;
52555         }
52556         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52557         var panelId = panel.getId();
52558         this.panels.removeKey(panelId);
52559         if(preservePanel){
52560             document.body.appendChild(panel.getEl().dom);
52561         }
52562         if(this.tabs){
52563             this.tabs.removeTab(panel.getEl().id);
52564         }else if (!preservePanel){
52565             this.bodyEl.dom.removeChild(panel.getEl().dom);
52566         }
52567         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52568             var p = this.panels.first();
52569             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52570             tempEl.appendChild(p.getEl().dom);
52571             this.bodyEl.update("");
52572             this.bodyEl.dom.appendChild(p.getEl().dom);
52573             tempEl = null;
52574             this.updateTitle(p.getTitle());
52575             this.tabs = null;
52576             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52577             this.setActivePanel(p);
52578         }
52579         panel.setRegion(null);
52580         if(this.activePanel == panel){
52581             this.activePanel = null;
52582         }
52583         if(this.config.autoDestroy !== false && preservePanel !== true){
52584             try{panel.destroy();}catch(e){}
52585         }
52586         this.fireEvent("panelremoved", this, panel);
52587         return panel;
52588     },
52589
52590     /**
52591      * Returns the TabPanel component used by this region
52592      * @return {Roo.TabPanel}
52593      */
52594     getTabs : function(){
52595         return this.tabs;
52596     },
52597
52598     createTool : function(parentEl, className){
52599         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52600             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52601         btn.addClassOnOver("x-layout-tools-button-over");
52602         return btn;
52603     }
52604 });/*
52605  * Based on:
52606  * Ext JS Library 1.1.1
52607  * Copyright(c) 2006-2007, Ext JS, LLC.
52608  *
52609  * Originally Released Under LGPL - original licence link has changed is not relivant.
52610  *
52611  * Fork - LGPL
52612  * <script type="text/javascript">
52613  */
52614  
52615
52616
52617 /**
52618  * @class Roo.SplitLayoutRegion
52619  * @extends Roo.LayoutRegion
52620  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52621  */
52622 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52623     this.cursor = cursor;
52624     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52625 };
52626
52627 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52628     splitTip : "Drag to resize.",
52629     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52630     useSplitTips : false,
52631
52632     applyConfig : function(config){
52633         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52634         if(config.split){
52635             if(!this.split){
52636                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52637                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52638                 /** The SplitBar for this region 
52639                 * @type Roo.SplitBar */
52640                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52641                 this.split.on("moved", this.onSplitMove, this);
52642                 this.split.useShim = config.useShim === true;
52643                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52644                 if(this.useSplitTips){
52645                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52646                 }
52647                 if(config.collapsible){
52648                     this.split.el.on("dblclick", this.collapse,  this);
52649                 }
52650             }
52651             if(typeof config.minSize != "undefined"){
52652                 this.split.minSize = config.minSize;
52653             }
52654             if(typeof config.maxSize != "undefined"){
52655                 this.split.maxSize = config.maxSize;
52656             }
52657             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52658                 this.hideSplitter();
52659             }
52660         }
52661     },
52662
52663     getHMaxSize : function(){
52664          var cmax = this.config.maxSize || 10000;
52665          var center = this.mgr.getRegion("center");
52666          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52667     },
52668
52669     getVMaxSize : function(){
52670          var cmax = this.config.maxSize || 10000;
52671          var center = this.mgr.getRegion("center");
52672          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52673     },
52674
52675     onSplitMove : function(split, newSize){
52676         this.fireEvent("resized", this, newSize);
52677     },
52678     
52679     /** 
52680      * Returns the {@link Roo.SplitBar} for this region.
52681      * @return {Roo.SplitBar}
52682      */
52683     getSplitBar : function(){
52684         return this.split;
52685     },
52686     
52687     hide : function(){
52688         this.hideSplitter();
52689         Roo.SplitLayoutRegion.superclass.hide.call(this);
52690     },
52691
52692     hideSplitter : function(){
52693         if(this.split){
52694             this.split.el.setLocation(-2000,-2000);
52695             this.split.el.hide();
52696         }
52697     },
52698
52699     show : function(){
52700         if(this.split){
52701             this.split.el.show();
52702         }
52703         Roo.SplitLayoutRegion.superclass.show.call(this);
52704     },
52705     
52706     beforeSlide: function(){
52707         if(Roo.isGecko){// firefox overflow auto bug workaround
52708             this.bodyEl.clip();
52709             if(this.tabs) {
52710                 this.tabs.bodyEl.clip();
52711             }
52712             if(this.activePanel){
52713                 this.activePanel.getEl().clip();
52714                 
52715                 if(this.activePanel.beforeSlide){
52716                     this.activePanel.beforeSlide();
52717                 }
52718             }
52719         }
52720     },
52721     
52722     afterSlide : function(){
52723         if(Roo.isGecko){// firefox overflow auto bug workaround
52724             this.bodyEl.unclip();
52725             if(this.tabs) {
52726                 this.tabs.bodyEl.unclip();
52727             }
52728             if(this.activePanel){
52729                 this.activePanel.getEl().unclip();
52730                 if(this.activePanel.afterSlide){
52731                     this.activePanel.afterSlide();
52732                 }
52733             }
52734         }
52735     },
52736
52737     initAutoHide : function(){
52738         if(this.autoHide !== false){
52739             if(!this.autoHideHd){
52740                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52741                 this.autoHideHd = {
52742                     "mouseout": function(e){
52743                         if(!e.within(this.el, true)){
52744                             st.delay(500);
52745                         }
52746                     },
52747                     "mouseover" : function(e){
52748                         st.cancel();
52749                     },
52750                     scope : this
52751                 };
52752             }
52753             this.el.on(this.autoHideHd);
52754         }
52755     },
52756
52757     clearAutoHide : function(){
52758         if(this.autoHide !== false){
52759             this.el.un("mouseout", this.autoHideHd.mouseout);
52760             this.el.un("mouseover", this.autoHideHd.mouseover);
52761         }
52762     },
52763
52764     clearMonitor : function(){
52765         Roo.get(document).un("click", this.slideInIf, this);
52766     },
52767
52768     // these names are backwards but not changed for compat
52769     slideOut : function(){
52770         if(this.isSlid || this.el.hasActiveFx()){
52771             return;
52772         }
52773         this.isSlid = true;
52774         if(this.collapseBtn){
52775             this.collapseBtn.hide();
52776         }
52777         this.closeBtnState = this.closeBtn.getStyle('display');
52778         this.closeBtn.hide();
52779         if(this.stickBtn){
52780             this.stickBtn.show();
52781         }
52782         this.el.show();
52783         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52784         this.beforeSlide();
52785         this.el.setStyle("z-index", 10001);
52786         this.el.slideIn(this.getSlideAnchor(), {
52787             callback: function(){
52788                 this.afterSlide();
52789                 this.initAutoHide();
52790                 Roo.get(document).on("click", this.slideInIf, this);
52791                 this.fireEvent("slideshow", this);
52792             },
52793             scope: this,
52794             block: true
52795         });
52796     },
52797
52798     afterSlideIn : function(){
52799         this.clearAutoHide();
52800         this.isSlid = false;
52801         this.clearMonitor();
52802         this.el.setStyle("z-index", "");
52803         if(this.collapseBtn){
52804             this.collapseBtn.show();
52805         }
52806         this.closeBtn.setStyle('display', this.closeBtnState);
52807         if(this.stickBtn){
52808             this.stickBtn.hide();
52809         }
52810         this.fireEvent("slidehide", this);
52811     },
52812
52813     slideIn : function(cb){
52814         if(!this.isSlid || this.el.hasActiveFx()){
52815             Roo.callback(cb);
52816             return;
52817         }
52818         this.isSlid = false;
52819         this.beforeSlide();
52820         this.el.slideOut(this.getSlideAnchor(), {
52821             callback: function(){
52822                 this.el.setLeftTop(-10000, -10000);
52823                 this.afterSlide();
52824                 this.afterSlideIn();
52825                 Roo.callback(cb);
52826             },
52827             scope: this,
52828             block: true
52829         });
52830     },
52831     
52832     slideInIf : function(e){
52833         if(!e.within(this.el)){
52834             this.slideIn();
52835         }
52836     },
52837
52838     animateCollapse : function(){
52839         this.beforeSlide();
52840         this.el.setStyle("z-index", 20000);
52841         var anchor = this.getSlideAnchor();
52842         this.el.slideOut(anchor, {
52843             callback : function(){
52844                 this.el.setStyle("z-index", "");
52845                 this.collapsedEl.slideIn(anchor, {duration:.3});
52846                 this.afterSlide();
52847                 this.el.setLocation(-10000,-10000);
52848                 this.el.hide();
52849                 this.fireEvent("collapsed", this);
52850             },
52851             scope: this,
52852             block: true
52853         });
52854     },
52855
52856     animateExpand : function(){
52857         this.beforeSlide();
52858         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52859         this.el.setStyle("z-index", 20000);
52860         this.collapsedEl.hide({
52861             duration:.1
52862         });
52863         this.el.slideIn(this.getSlideAnchor(), {
52864             callback : function(){
52865                 this.el.setStyle("z-index", "");
52866                 this.afterSlide();
52867                 if(this.split){
52868                     this.split.el.show();
52869                 }
52870                 this.fireEvent("invalidated", this);
52871                 this.fireEvent("expanded", this);
52872             },
52873             scope: this,
52874             block: true
52875         });
52876     },
52877
52878     anchors : {
52879         "west" : "left",
52880         "east" : "right",
52881         "north" : "top",
52882         "south" : "bottom"
52883     },
52884
52885     sanchors : {
52886         "west" : "l",
52887         "east" : "r",
52888         "north" : "t",
52889         "south" : "b"
52890     },
52891
52892     canchors : {
52893         "west" : "tl-tr",
52894         "east" : "tr-tl",
52895         "north" : "tl-bl",
52896         "south" : "bl-tl"
52897     },
52898
52899     getAnchor : function(){
52900         return this.anchors[this.position];
52901     },
52902
52903     getCollapseAnchor : function(){
52904         return this.canchors[this.position];
52905     },
52906
52907     getSlideAnchor : function(){
52908         return this.sanchors[this.position];
52909     },
52910
52911     getAlignAdj : function(){
52912         var cm = this.cmargins;
52913         switch(this.position){
52914             case "west":
52915                 return [0, 0];
52916             break;
52917             case "east":
52918                 return [0, 0];
52919             break;
52920             case "north":
52921                 return [0, 0];
52922             break;
52923             case "south":
52924                 return [0, 0];
52925             break;
52926         }
52927     },
52928
52929     getExpandAdj : function(){
52930         var c = this.collapsedEl, cm = this.cmargins;
52931         switch(this.position){
52932             case "west":
52933                 return [-(cm.right+c.getWidth()+cm.left), 0];
52934             break;
52935             case "east":
52936                 return [cm.right+c.getWidth()+cm.left, 0];
52937             break;
52938             case "north":
52939                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52940             break;
52941             case "south":
52942                 return [0, cm.top+cm.bottom+c.getHeight()];
52943             break;
52944         }
52945     }
52946 });/*
52947  * Based on:
52948  * Ext JS Library 1.1.1
52949  * Copyright(c) 2006-2007, Ext JS, LLC.
52950  *
52951  * Originally Released Under LGPL - original licence link has changed is not relivant.
52952  *
52953  * Fork - LGPL
52954  * <script type="text/javascript">
52955  */
52956 /*
52957  * These classes are private internal classes
52958  */
52959 Roo.CenterLayoutRegion = function(mgr, config){
52960     Roo.LayoutRegion.call(this, mgr, config, "center");
52961     this.visible = true;
52962     this.minWidth = config.minWidth || 20;
52963     this.minHeight = config.minHeight || 20;
52964 };
52965
52966 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52967     hide : function(){
52968         // center panel can't be hidden
52969     },
52970     
52971     show : function(){
52972         // center panel can't be hidden
52973     },
52974     
52975     getMinWidth: function(){
52976         return this.minWidth;
52977     },
52978     
52979     getMinHeight: function(){
52980         return this.minHeight;
52981     }
52982 });
52983
52984
52985 Roo.NorthLayoutRegion = function(mgr, config){
52986     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52987     if(this.split){
52988         this.split.placement = Roo.SplitBar.TOP;
52989         this.split.orientation = Roo.SplitBar.VERTICAL;
52990         this.split.el.addClass("x-layout-split-v");
52991     }
52992     var size = config.initialSize || config.height;
52993     if(typeof size != "undefined"){
52994         this.el.setHeight(size);
52995     }
52996 };
52997 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52998     orientation: Roo.SplitBar.VERTICAL,
52999     getBox : function(){
53000         if(this.collapsed){
53001             return this.collapsedEl.getBox();
53002         }
53003         var box = this.el.getBox();
53004         if(this.split){
53005             box.height += this.split.el.getHeight();
53006         }
53007         return box;
53008     },
53009     
53010     updateBox : function(box){
53011         if(this.split && !this.collapsed){
53012             box.height -= this.split.el.getHeight();
53013             this.split.el.setLeft(box.x);
53014             this.split.el.setTop(box.y+box.height);
53015             this.split.el.setWidth(box.width);
53016         }
53017         if(this.collapsed){
53018             this.updateBody(box.width, null);
53019         }
53020         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53021     }
53022 });
53023
53024 Roo.SouthLayoutRegion = function(mgr, config){
53025     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53026     if(this.split){
53027         this.split.placement = Roo.SplitBar.BOTTOM;
53028         this.split.orientation = Roo.SplitBar.VERTICAL;
53029         this.split.el.addClass("x-layout-split-v");
53030     }
53031     var size = config.initialSize || config.height;
53032     if(typeof size != "undefined"){
53033         this.el.setHeight(size);
53034     }
53035 };
53036 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53037     orientation: Roo.SplitBar.VERTICAL,
53038     getBox : function(){
53039         if(this.collapsed){
53040             return this.collapsedEl.getBox();
53041         }
53042         var box = this.el.getBox();
53043         if(this.split){
53044             var sh = this.split.el.getHeight();
53045             box.height += sh;
53046             box.y -= sh;
53047         }
53048         return box;
53049     },
53050     
53051     updateBox : function(box){
53052         if(this.split && !this.collapsed){
53053             var sh = this.split.el.getHeight();
53054             box.height -= sh;
53055             box.y += sh;
53056             this.split.el.setLeft(box.x);
53057             this.split.el.setTop(box.y-sh);
53058             this.split.el.setWidth(box.width);
53059         }
53060         if(this.collapsed){
53061             this.updateBody(box.width, null);
53062         }
53063         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53064     }
53065 });
53066
53067 Roo.EastLayoutRegion = function(mgr, config){
53068     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53069     if(this.split){
53070         this.split.placement = Roo.SplitBar.RIGHT;
53071         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53072         this.split.el.addClass("x-layout-split-h");
53073     }
53074     var size = config.initialSize || config.width;
53075     if(typeof size != "undefined"){
53076         this.el.setWidth(size);
53077     }
53078 };
53079 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53080     orientation: Roo.SplitBar.HORIZONTAL,
53081     getBox : function(){
53082         if(this.collapsed){
53083             return this.collapsedEl.getBox();
53084         }
53085         var box = this.el.getBox();
53086         if(this.split){
53087             var sw = this.split.el.getWidth();
53088             box.width += sw;
53089             box.x -= sw;
53090         }
53091         return box;
53092     },
53093
53094     updateBox : function(box){
53095         if(this.split && !this.collapsed){
53096             var sw = this.split.el.getWidth();
53097             box.width -= sw;
53098             this.split.el.setLeft(box.x);
53099             this.split.el.setTop(box.y);
53100             this.split.el.setHeight(box.height);
53101             box.x += sw;
53102         }
53103         if(this.collapsed){
53104             this.updateBody(null, box.height);
53105         }
53106         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53107     }
53108 });
53109
53110 Roo.WestLayoutRegion = function(mgr, config){
53111     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53112     if(this.split){
53113         this.split.placement = Roo.SplitBar.LEFT;
53114         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53115         this.split.el.addClass("x-layout-split-h");
53116     }
53117     var size = config.initialSize || config.width;
53118     if(typeof size != "undefined"){
53119         this.el.setWidth(size);
53120     }
53121 };
53122 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53123     orientation: Roo.SplitBar.HORIZONTAL,
53124     getBox : function(){
53125         if(this.collapsed){
53126             return this.collapsedEl.getBox();
53127         }
53128         var box = this.el.getBox();
53129         if(this.split){
53130             box.width += this.split.el.getWidth();
53131         }
53132         return box;
53133     },
53134     
53135     updateBox : function(box){
53136         if(this.split && !this.collapsed){
53137             var sw = this.split.el.getWidth();
53138             box.width -= sw;
53139             this.split.el.setLeft(box.x+box.width);
53140             this.split.el.setTop(box.y);
53141             this.split.el.setHeight(box.height);
53142         }
53143         if(this.collapsed){
53144             this.updateBody(null, box.height);
53145         }
53146         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53147     }
53148 });
53149 /*
53150  * Based on:
53151  * Ext JS Library 1.1.1
53152  * Copyright(c) 2006-2007, Ext JS, LLC.
53153  *
53154  * Originally Released Under LGPL - original licence link has changed is not relivant.
53155  *
53156  * Fork - LGPL
53157  * <script type="text/javascript">
53158  */
53159  
53160  
53161 /*
53162  * Private internal class for reading and applying state
53163  */
53164 Roo.LayoutStateManager = function(layout){
53165      // default empty state
53166      this.state = {
53167         north: {},
53168         south: {},
53169         east: {},
53170         west: {}       
53171     };
53172 };
53173
53174 Roo.LayoutStateManager.prototype = {
53175     init : function(layout, provider){
53176         this.provider = provider;
53177         var state = provider.get(layout.id+"-layout-state");
53178         if(state){
53179             var wasUpdating = layout.isUpdating();
53180             if(!wasUpdating){
53181                 layout.beginUpdate();
53182             }
53183             for(var key in state){
53184                 if(typeof state[key] != "function"){
53185                     var rstate = state[key];
53186                     var r = layout.getRegion(key);
53187                     if(r && rstate){
53188                         if(rstate.size){
53189                             r.resizeTo(rstate.size);
53190                         }
53191                         if(rstate.collapsed == true){
53192                             r.collapse(true);
53193                         }else{
53194                             r.expand(null, true);
53195                         }
53196                     }
53197                 }
53198             }
53199             if(!wasUpdating){
53200                 layout.endUpdate();
53201             }
53202             this.state = state; 
53203         }
53204         this.layout = layout;
53205         layout.on("regionresized", this.onRegionResized, this);
53206         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53207         layout.on("regionexpanded", this.onRegionExpanded, this);
53208     },
53209     
53210     storeState : function(){
53211         this.provider.set(this.layout.id+"-layout-state", this.state);
53212     },
53213     
53214     onRegionResized : function(region, newSize){
53215         this.state[region.getPosition()].size = newSize;
53216         this.storeState();
53217     },
53218     
53219     onRegionCollapsed : function(region){
53220         this.state[region.getPosition()].collapsed = true;
53221         this.storeState();
53222     },
53223     
53224     onRegionExpanded : function(region){
53225         this.state[region.getPosition()].collapsed = false;
53226         this.storeState();
53227     }
53228 };/*
53229  * Based on:
53230  * Ext JS Library 1.1.1
53231  * Copyright(c) 2006-2007, Ext JS, LLC.
53232  *
53233  * Originally Released Under LGPL - original licence link has changed is not relivant.
53234  *
53235  * Fork - LGPL
53236  * <script type="text/javascript">
53237  */
53238 /**
53239  * @class Roo.ContentPanel
53240  * @extends Roo.util.Observable
53241  * A basic ContentPanel element.
53242  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53243  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53244  * @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
53245  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53246  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53247  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53248  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53249  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53250  * @cfg {String} title          The title for this panel
53251  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53252  * @cfg {String} url            Calls {@link #setUrl} with this value
53253  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53254  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53255  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53256  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53257
53258  * @constructor
53259  * Create a new ContentPanel.
53260  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53261  * @param {String/Object} config A string to set only the title or a config object
53262  * @param {String} content (optional) Set the HTML content for this panel
53263  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53264  */
53265 Roo.ContentPanel = function(el, config, content){
53266     
53267      
53268     /*
53269     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53270         config = el;
53271         el = Roo.id();
53272     }
53273     if (config && config.parentLayout) { 
53274         el = config.parentLayout.el.createChild(); 
53275     }
53276     */
53277     if(el.autoCreate){ // xtype is available if this is called from factory
53278         config = el;
53279         el = Roo.id();
53280     }
53281     this.el = Roo.get(el);
53282     if(!this.el && config && config.autoCreate){
53283         if(typeof config.autoCreate == "object"){
53284             if(!config.autoCreate.id){
53285                 config.autoCreate.id = config.id||el;
53286             }
53287             this.el = Roo.DomHelper.append(document.body,
53288                         config.autoCreate, true);
53289         }else{
53290             this.el = Roo.DomHelper.append(document.body,
53291                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53292         }
53293     }
53294     this.closable = false;
53295     this.loaded = false;
53296     this.active = false;
53297     if(typeof config == "string"){
53298         this.title = config;
53299     }else{
53300         Roo.apply(this, config);
53301     }
53302     
53303     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53304         this.wrapEl = this.el.wrap();
53305         this.toolbar.container = this.el.insertSibling(false, 'before');
53306         this.toolbar = new Roo.Toolbar(this.toolbar);
53307     }
53308     
53309     // xtype created footer. - not sure if will work as we normally have to render first..
53310     if (this.footer && !this.footer.el && this.footer.xtype) {
53311         if (!this.wrapEl) {
53312             this.wrapEl = this.el.wrap();
53313         }
53314     
53315         this.footer.container = this.wrapEl.createChild();
53316          
53317         this.footer = Roo.factory(this.footer, Roo);
53318         
53319     }
53320     
53321     if(this.resizeEl){
53322         this.resizeEl = Roo.get(this.resizeEl, true);
53323     }else{
53324         this.resizeEl = this.el;
53325     }
53326     // handle view.xtype
53327     
53328  
53329     
53330     
53331     this.addEvents({
53332         /**
53333          * @event activate
53334          * Fires when this panel is activated. 
53335          * @param {Roo.ContentPanel} this
53336          */
53337         "activate" : true,
53338         /**
53339          * @event deactivate
53340          * Fires when this panel is activated. 
53341          * @param {Roo.ContentPanel} this
53342          */
53343         "deactivate" : true,
53344
53345         /**
53346          * @event resize
53347          * Fires when this panel is resized if fitToFrame is true.
53348          * @param {Roo.ContentPanel} this
53349          * @param {Number} width The width after any component adjustments
53350          * @param {Number} height The height after any component adjustments
53351          */
53352         "resize" : true,
53353         
53354          /**
53355          * @event render
53356          * Fires when this tab is created
53357          * @param {Roo.ContentPanel} this
53358          */
53359         "render" : true
53360         
53361         
53362         
53363     });
53364     
53365
53366     
53367     
53368     if(this.autoScroll){
53369         this.resizeEl.setStyle("overflow", "auto");
53370     } else {
53371         // fix randome scrolling
53372         this.el.on('scroll', function() {
53373             Roo.log('fix random scolling');
53374             this.scrollTo('top',0); 
53375         });
53376     }
53377     content = content || this.content;
53378     if(content){
53379         this.setContent(content);
53380     }
53381     if(config && config.url){
53382         this.setUrl(this.url, this.params, this.loadOnce);
53383     }
53384     
53385     
53386     
53387     Roo.ContentPanel.superclass.constructor.call(this);
53388     
53389     if (this.view && typeof(this.view.xtype) != 'undefined') {
53390         this.view.el = this.el.appendChild(document.createElement("div"));
53391         this.view = Roo.factory(this.view); 
53392         this.view.render  &&  this.view.render(false, '');  
53393     }
53394     
53395     
53396     this.fireEvent('render', this);
53397 };
53398
53399 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53400     tabTip:'',
53401     setRegion : function(region){
53402         this.region = region;
53403         if(region){
53404            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53405         }else{
53406            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53407         } 
53408     },
53409     
53410     /**
53411      * Returns the toolbar for this Panel if one was configured. 
53412      * @return {Roo.Toolbar} 
53413      */
53414     getToolbar : function(){
53415         return this.toolbar;
53416     },
53417     
53418     setActiveState : function(active){
53419         this.active = active;
53420         if(!active){
53421             this.fireEvent("deactivate", this);
53422         }else{
53423             this.fireEvent("activate", this);
53424         }
53425     },
53426     /**
53427      * Updates this panel's element
53428      * @param {String} content The new content
53429      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53430     */
53431     setContent : function(content, loadScripts){
53432         this.el.update(content, loadScripts);
53433     },
53434
53435     ignoreResize : function(w, h){
53436         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53437             return true;
53438         }else{
53439             this.lastSize = {width: w, height: h};
53440             return false;
53441         }
53442     },
53443     /**
53444      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53445      * @return {Roo.UpdateManager} The UpdateManager
53446      */
53447     getUpdateManager : function(){
53448         return this.el.getUpdateManager();
53449     },
53450      /**
53451      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53452      * @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:
53453 <pre><code>
53454 panel.load({
53455     url: "your-url.php",
53456     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53457     callback: yourFunction,
53458     scope: yourObject, //(optional scope)
53459     discardUrl: false,
53460     nocache: false,
53461     text: "Loading...",
53462     timeout: 30,
53463     scripts: false
53464 });
53465 </code></pre>
53466      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53467      * 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.
53468      * @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}
53469      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53470      * @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.
53471      * @return {Roo.ContentPanel} this
53472      */
53473     load : function(){
53474         var um = this.el.getUpdateManager();
53475         um.update.apply(um, arguments);
53476         return this;
53477     },
53478
53479
53480     /**
53481      * 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.
53482      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53483      * @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)
53484      * @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)
53485      * @return {Roo.UpdateManager} The UpdateManager
53486      */
53487     setUrl : function(url, params, loadOnce){
53488         if(this.refreshDelegate){
53489             this.removeListener("activate", this.refreshDelegate);
53490         }
53491         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53492         this.on("activate", this.refreshDelegate);
53493         return this.el.getUpdateManager();
53494     },
53495     
53496     _handleRefresh : function(url, params, loadOnce){
53497         if(!loadOnce || !this.loaded){
53498             var updater = this.el.getUpdateManager();
53499             updater.update(url, params, this._setLoaded.createDelegate(this));
53500         }
53501     },
53502     
53503     _setLoaded : function(){
53504         this.loaded = true;
53505     }, 
53506     
53507     /**
53508      * Returns this panel's id
53509      * @return {String} 
53510      */
53511     getId : function(){
53512         return this.el.id;
53513     },
53514     
53515     /** 
53516      * Returns this panel's element - used by regiosn to add.
53517      * @return {Roo.Element} 
53518      */
53519     getEl : function(){
53520         return this.wrapEl || this.el;
53521     },
53522     
53523     adjustForComponents : function(width, height)
53524     {
53525         //Roo.log('adjustForComponents ');
53526         if(this.resizeEl != this.el){
53527             width -= this.el.getFrameWidth('lr');
53528             height -= this.el.getFrameWidth('tb');
53529         }
53530         if(this.toolbar){
53531             var te = this.toolbar.getEl();
53532             height -= te.getHeight();
53533             te.setWidth(width);
53534         }
53535         if(this.footer){
53536             var te = this.footer.getEl();
53537             Roo.log("footer:" + te.getHeight());
53538             
53539             height -= te.getHeight();
53540             te.setWidth(width);
53541         }
53542         
53543         
53544         if(this.adjustments){
53545             width += this.adjustments[0];
53546             height += this.adjustments[1];
53547         }
53548         return {"width": width, "height": height};
53549     },
53550     
53551     setSize : function(width, height){
53552         if(this.fitToFrame && !this.ignoreResize(width, height)){
53553             if(this.fitContainer && this.resizeEl != this.el){
53554                 this.el.setSize(width, height);
53555             }
53556             var size = this.adjustForComponents(width, height);
53557             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53558             this.fireEvent('resize', this, size.width, size.height);
53559         }
53560     },
53561     
53562     /**
53563      * Returns this panel's title
53564      * @return {String} 
53565      */
53566     getTitle : function(){
53567         return this.title;
53568     },
53569     
53570     /**
53571      * Set this panel's title
53572      * @param {String} title
53573      */
53574     setTitle : function(title){
53575         this.title = title;
53576         if(this.region){
53577             this.region.updatePanelTitle(this, title);
53578         }
53579     },
53580     
53581     /**
53582      * Returns true is this panel was configured to be closable
53583      * @return {Boolean} 
53584      */
53585     isClosable : function(){
53586         return this.closable;
53587     },
53588     
53589     beforeSlide : function(){
53590         this.el.clip();
53591         this.resizeEl.clip();
53592     },
53593     
53594     afterSlide : function(){
53595         this.el.unclip();
53596         this.resizeEl.unclip();
53597     },
53598     
53599     /**
53600      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53601      *   Will fail silently if the {@link #setUrl} method has not been called.
53602      *   This does not activate the panel, just updates its content.
53603      */
53604     refresh : function(){
53605         if(this.refreshDelegate){
53606            this.loaded = false;
53607            this.refreshDelegate();
53608         }
53609     },
53610     
53611     /**
53612      * Destroys this panel
53613      */
53614     destroy : function(){
53615         this.el.removeAllListeners();
53616         var tempEl = document.createElement("span");
53617         tempEl.appendChild(this.el.dom);
53618         tempEl.innerHTML = "";
53619         this.el.remove();
53620         this.el = null;
53621     },
53622     
53623     /**
53624      * form - if the content panel contains a form - this is a reference to it.
53625      * @type {Roo.form.Form}
53626      */
53627     form : false,
53628     /**
53629      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53630      *    This contains a reference to it.
53631      * @type {Roo.View}
53632      */
53633     view : false,
53634     
53635       /**
53636      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53637      * <pre><code>
53638
53639 layout.addxtype({
53640        xtype : 'Form',
53641        items: [ .... ]
53642    }
53643 );
53644
53645 </code></pre>
53646      * @param {Object} cfg Xtype definition of item to add.
53647      */
53648     
53649     addxtype : function(cfg) {
53650         // add form..
53651         if (cfg.xtype.match(/^Form$/)) {
53652             
53653             var el;
53654             //if (this.footer) {
53655             //    el = this.footer.container.insertSibling(false, 'before');
53656             //} else {
53657                 el = this.el.createChild();
53658             //}
53659
53660             this.form = new  Roo.form.Form(cfg);
53661             
53662             
53663             if ( this.form.allItems.length) {
53664                 this.form.render(el.dom);
53665             }
53666             return this.form;
53667         }
53668         // should only have one of theses..
53669         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53670             // views.. should not be just added - used named prop 'view''
53671             
53672             cfg.el = this.el.appendChild(document.createElement("div"));
53673             // factory?
53674             
53675             var ret = new Roo.factory(cfg);
53676              
53677              ret.render && ret.render(false, ''); // render blank..
53678             this.view = ret;
53679             return ret;
53680         }
53681         return false;
53682     }
53683 });
53684
53685 /**
53686  * @class Roo.GridPanel
53687  * @extends Roo.ContentPanel
53688  * @constructor
53689  * Create a new GridPanel.
53690  * @param {Roo.grid.Grid} grid The grid for this panel
53691  * @param {String/Object} config A string to set only the panel's title, or a config object
53692  */
53693 Roo.GridPanel = function(grid, config){
53694     
53695   
53696     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53697         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53698         
53699     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53700     
53701     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53702     
53703     if(this.toolbar){
53704         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53705     }
53706     // xtype created footer. - not sure if will work as we normally have to render first..
53707     if (this.footer && !this.footer.el && this.footer.xtype) {
53708         
53709         this.footer.container = this.grid.getView().getFooterPanel(true);
53710         this.footer.dataSource = this.grid.dataSource;
53711         this.footer = Roo.factory(this.footer, Roo);
53712         
53713     }
53714     
53715     grid.monitorWindowResize = false; // turn off autosizing
53716     grid.autoHeight = false;
53717     grid.autoWidth = false;
53718     this.grid = grid;
53719     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53720 };
53721
53722 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53723     getId : function(){
53724         return this.grid.id;
53725     },
53726     
53727     /**
53728      * Returns the grid for this panel
53729      * @return {Roo.grid.Grid} 
53730      */
53731     getGrid : function(){
53732         return this.grid;    
53733     },
53734     
53735     setSize : function(width, height){
53736         if(!this.ignoreResize(width, height)){
53737             var grid = this.grid;
53738             var size = this.adjustForComponents(width, height);
53739             grid.getGridEl().setSize(size.width, size.height);
53740             grid.autoSize();
53741         }
53742     },
53743     
53744     beforeSlide : function(){
53745         this.grid.getView().scroller.clip();
53746     },
53747     
53748     afterSlide : function(){
53749         this.grid.getView().scroller.unclip();
53750     },
53751     
53752     destroy : function(){
53753         this.grid.destroy();
53754         delete this.grid;
53755         Roo.GridPanel.superclass.destroy.call(this); 
53756     }
53757 });
53758
53759
53760 /**
53761  * @class Roo.NestedLayoutPanel
53762  * @extends Roo.ContentPanel
53763  * @constructor
53764  * Create a new NestedLayoutPanel.
53765  * 
53766  * 
53767  * @param {Roo.BorderLayout} layout The layout for this panel
53768  * @param {String/Object} config A string to set only the title or a config object
53769  */
53770 Roo.NestedLayoutPanel = function(layout, config)
53771 {
53772     // construct with only one argument..
53773     /* FIXME - implement nicer consturctors
53774     if (layout.layout) {
53775         config = layout;
53776         layout = config.layout;
53777         delete config.layout;
53778     }
53779     if (layout.xtype && !layout.getEl) {
53780         // then layout needs constructing..
53781         layout = Roo.factory(layout, Roo);
53782     }
53783     */
53784     
53785     
53786     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53787     
53788     layout.monitorWindowResize = false; // turn off autosizing
53789     this.layout = layout;
53790     this.layout.getEl().addClass("x-layout-nested-layout");
53791     
53792     
53793     
53794     
53795 };
53796
53797 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53798
53799     setSize : function(width, height){
53800         if(!this.ignoreResize(width, height)){
53801             var size = this.adjustForComponents(width, height);
53802             var el = this.layout.getEl();
53803             el.setSize(size.width, size.height);
53804             var touch = el.dom.offsetWidth;
53805             this.layout.layout();
53806             // ie requires a double layout on the first pass
53807             if(Roo.isIE && !this.initialized){
53808                 this.initialized = true;
53809                 this.layout.layout();
53810             }
53811         }
53812     },
53813     
53814     // activate all subpanels if not currently active..
53815     
53816     setActiveState : function(active){
53817         this.active = active;
53818         if(!active){
53819             this.fireEvent("deactivate", this);
53820             return;
53821         }
53822         
53823         this.fireEvent("activate", this);
53824         // not sure if this should happen before or after..
53825         if (!this.layout) {
53826             return; // should not happen..
53827         }
53828         var reg = false;
53829         for (var r in this.layout.regions) {
53830             reg = this.layout.getRegion(r);
53831             if (reg.getActivePanel()) {
53832                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53833                 reg.setActivePanel(reg.getActivePanel());
53834                 continue;
53835             }
53836             if (!reg.panels.length) {
53837                 continue;
53838             }
53839             reg.showPanel(reg.getPanel(0));
53840         }
53841         
53842         
53843         
53844         
53845     },
53846     
53847     /**
53848      * Returns the nested BorderLayout for this panel
53849      * @return {Roo.BorderLayout} 
53850      */
53851     getLayout : function(){
53852         return this.layout;
53853     },
53854     
53855      /**
53856      * Adds a xtype elements to the layout of the nested panel
53857      * <pre><code>
53858
53859 panel.addxtype({
53860        xtype : 'ContentPanel',
53861        region: 'west',
53862        items: [ .... ]
53863    }
53864 );
53865
53866 panel.addxtype({
53867         xtype : 'NestedLayoutPanel',
53868         region: 'west',
53869         layout: {
53870            center: { },
53871            west: { }   
53872         },
53873         items : [ ... list of content panels or nested layout panels.. ]
53874    }
53875 );
53876 </code></pre>
53877      * @param {Object} cfg Xtype definition of item to add.
53878      */
53879     addxtype : function(cfg) {
53880         return this.layout.addxtype(cfg);
53881     
53882     }
53883 });
53884
53885 Roo.ScrollPanel = function(el, config, content){
53886     config = config || {};
53887     config.fitToFrame = true;
53888     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53889     
53890     this.el.dom.style.overflow = "hidden";
53891     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53892     this.el.removeClass("x-layout-inactive-content");
53893     this.el.on("mousewheel", this.onWheel, this);
53894
53895     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53896     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53897     up.unselectable(); down.unselectable();
53898     up.on("click", this.scrollUp, this);
53899     down.on("click", this.scrollDown, this);
53900     up.addClassOnOver("x-scroller-btn-over");
53901     down.addClassOnOver("x-scroller-btn-over");
53902     up.addClassOnClick("x-scroller-btn-click");
53903     down.addClassOnClick("x-scroller-btn-click");
53904     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53905
53906     this.resizeEl = this.el;
53907     this.el = wrap; this.up = up; this.down = down;
53908 };
53909
53910 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53911     increment : 100,
53912     wheelIncrement : 5,
53913     scrollUp : function(){
53914         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53915     },
53916
53917     scrollDown : function(){
53918         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53919     },
53920
53921     afterScroll : function(){
53922         var el = this.resizeEl;
53923         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53924         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53925         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53926     },
53927
53928     setSize : function(){
53929         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53930         this.afterScroll();
53931     },
53932
53933     onWheel : function(e){
53934         var d = e.getWheelDelta();
53935         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53936         this.afterScroll();
53937         e.stopEvent();
53938     },
53939
53940     setContent : function(content, loadScripts){
53941         this.resizeEl.update(content, loadScripts);
53942     }
53943
53944 });
53945
53946
53947
53948
53949
53950
53951
53952
53953
53954 /**
53955  * @class Roo.TreePanel
53956  * @extends Roo.ContentPanel
53957  * @constructor
53958  * Create a new TreePanel. - defaults to fit/scoll contents.
53959  * @param {String/Object} config A string to set only the panel's title, or a config object
53960  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53961  */
53962 Roo.TreePanel = function(config){
53963     var el = config.el;
53964     var tree = config.tree;
53965     delete config.tree; 
53966     delete config.el; // hopefull!
53967     
53968     // wrapper for IE7 strict & safari scroll issue
53969     
53970     var treeEl = el.createChild();
53971     config.resizeEl = treeEl;
53972     
53973     
53974     
53975     Roo.TreePanel.superclass.constructor.call(this, el, config);
53976  
53977  
53978     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53979     //console.log(tree);
53980     this.on('activate', function()
53981     {
53982         if (this.tree.rendered) {
53983             return;
53984         }
53985         //console.log('render tree');
53986         this.tree.render();
53987     });
53988     // this should not be needed.. - it's actually the 'el' that resizes?
53989     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53990     
53991     //this.on('resize',  function (cp, w, h) {
53992     //        this.tree.innerCt.setWidth(w);
53993     //        this.tree.innerCt.setHeight(h);
53994     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53995     //});
53996
53997         
53998     
53999 };
54000
54001 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54002     fitToFrame : true,
54003     autoScroll : true
54004 });
54005
54006
54007
54008
54009
54010
54011
54012
54013
54014
54015
54016 /*
54017  * Based on:
54018  * Ext JS Library 1.1.1
54019  * Copyright(c) 2006-2007, Ext JS, LLC.
54020  *
54021  * Originally Released Under LGPL - original licence link has changed is not relivant.
54022  *
54023  * Fork - LGPL
54024  * <script type="text/javascript">
54025  */
54026  
54027
54028 /**
54029  * @class Roo.ReaderLayout
54030  * @extends Roo.BorderLayout
54031  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54032  * center region containing two nested regions (a top one for a list view and one for item preview below),
54033  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54034  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54035  * expedites the setup of the overall layout and regions for this common application style.
54036  * Example:
54037  <pre><code>
54038 var reader = new Roo.ReaderLayout();
54039 var CP = Roo.ContentPanel;  // shortcut for adding
54040
54041 reader.beginUpdate();
54042 reader.add("north", new CP("north", "North"));
54043 reader.add("west", new CP("west", {title: "West"}));
54044 reader.add("east", new CP("east", {title: "East"}));
54045
54046 reader.regions.listView.add(new CP("listView", "List"));
54047 reader.regions.preview.add(new CP("preview", "Preview"));
54048 reader.endUpdate();
54049 </code></pre>
54050 * @constructor
54051 * Create a new ReaderLayout
54052 * @param {Object} config Configuration options
54053 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54054 * document.body if omitted)
54055 */
54056 Roo.ReaderLayout = function(config, renderTo){
54057     var c = config || {size:{}};
54058     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54059         north: c.north !== false ? Roo.apply({
54060             split:false,
54061             initialSize: 32,
54062             titlebar: false
54063         }, c.north) : false,
54064         west: c.west !== false ? Roo.apply({
54065             split:true,
54066             initialSize: 200,
54067             minSize: 175,
54068             maxSize: 400,
54069             titlebar: true,
54070             collapsible: true,
54071             animate: true,
54072             margins:{left:5,right:0,bottom:5,top:5},
54073             cmargins:{left:5,right:5,bottom:5,top:5}
54074         }, c.west) : false,
54075         east: c.east !== false ? Roo.apply({
54076             split:true,
54077             initialSize: 200,
54078             minSize: 175,
54079             maxSize: 400,
54080             titlebar: true,
54081             collapsible: true,
54082             animate: true,
54083             margins:{left:0,right:5,bottom:5,top:5},
54084             cmargins:{left:5,right:5,bottom:5,top:5}
54085         }, c.east) : false,
54086         center: Roo.apply({
54087             tabPosition: 'top',
54088             autoScroll:false,
54089             closeOnTab: true,
54090             titlebar:false,
54091             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54092         }, c.center)
54093     });
54094
54095     this.el.addClass('x-reader');
54096
54097     this.beginUpdate();
54098
54099     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54100         south: c.preview !== false ? Roo.apply({
54101             split:true,
54102             initialSize: 200,
54103             minSize: 100,
54104             autoScroll:true,
54105             collapsible:true,
54106             titlebar: true,
54107             cmargins:{top:5,left:0, right:0, bottom:0}
54108         }, c.preview) : false,
54109         center: Roo.apply({
54110             autoScroll:false,
54111             titlebar:false,
54112             minHeight:200
54113         }, c.listView)
54114     });
54115     this.add('center', new Roo.NestedLayoutPanel(inner,
54116             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54117
54118     this.endUpdate();
54119
54120     this.regions.preview = inner.getRegion('south');
54121     this.regions.listView = inner.getRegion('center');
54122 };
54123
54124 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54125  * Based on:
54126  * Ext JS Library 1.1.1
54127  * Copyright(c) 2006-2007, Ext JS, LLC.
54128  *
54129  * Originally Released Under LGPL - original licence link has changed is not relivant.
54130  *
54131  * Fork - LGPL
54132  * <script type="text/javascript">
54133  */
54134  
54135 /**
54136  * @class Roo.grid.Grid
54137  * @extends Roo.util.Observable
54138  * This class represents the primary interface of a component based grid control.
54139  * <br><br>Usage:<pre><code>
54140  var grid = new Roo.grid.Grid("my-container-id", {
54141      ds: myDataStore,
54142      cm: myColModel,
54143      selModel: mySelectionModel,
54144      autoSizeColumns: true,
54145      monitorWindowResize: false,
54146      trackMouseOver: true
54147  });
54148  // set any options
54149  grid.render();
54150  * </code></pre>
54151  * <b>Common Problems:</b><br/>
54152  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54153  * element will correct this<br/>
54154  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54155  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54156  * are unpredictable.<br/>
54157  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54158  * grid to calculate dimensions/offsets.<br/>
54159   * @constructor
54160  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54161  * The container MUST have some type of size defined for the grid to fill. The container will be
54162  * automatically set to position relative if it isn't already.
54163  * @param {Object} config A config object that sets properties on this grid.
54164  */
54165 Roo.grid.Grid = function(container, config){
54166         // initialize the container
54167         this.container = Roo.get(container);
54168         this.container.update("");
54169         this.container.setStyle("overflow", "hidden");
54170     this.container.addClass('x-grid-container');
54171
54172     this.id = this.container.id;
54173
54174     Roo.apply(this, config);
54175     // check and correct shorthanded configs
54176     if(this.ds){
54177         this.dataSource = this.ds;
54178         delete this.ds;
54179     }
54180     if(this.cm){
54181         this.colModel = this.cm;
54182         delete this.cm;
54183     }
54184     if(this.sm){
54185         this.selModel = this.sm;
54186         delete this.sm;
54187     }
54188
54189     if (this.selModel) {
54190         this.selModel = Roo.factory(this.selModel, Roo.grid);
54191         this.sm = this.selModel;
54192         this.sm.xmodule = this.xmodule || false;
54193     }
54194     if (typeof(this.colModel.config) == 'undefined') {
54195         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54196         this.cm = this.colModel;
54197         this.cm.xmodule = this.xmodule || false;
54198     }
54199     if (this.dataSource) {
54200         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54201         this.ds = this.dataSource;
54202         this.ds.xmodule = this.xmodule || false;
54203          
54204     }
54205     
54206     
54207     
54208     if(this.width){
54209         this.container.setWidth(this.width);
54210     }
54211
54212     if(this.height){
54213         this.container.setHeight(this.height);
54214     }
54215     /** @private */
54216         this.addEvents({
54217         // raw events
54218         /**
54219          * @event click
54220          * The raw click event for the entire grid.
54221          * @param {Roo.EventObject} e
54222          */
54223         "click" : true,
54224         /**
54225          * @event dblclick
54226          * The raw dblclick event for the entire grid.
54227          * @param {Roo.EventObject} e
54228          */
54229         "dblclick" : true,
54230         /**
54231          * @event contextmenu
54232          * The raw contextmenu event for the entire grid.
54233          * @param {Roo.EventObject} e
54234          */
54235         "contextmenu" : true,
54236         /**
54237          * @event mousedown
54238          * The raw mousedown event for the entire grid.
54239          * @param {Roo.EventObject} e
54240          */
54241         "mousedown" : true,
54242         /**
54243          * @event mouseup
54244          * The raw mouseup event for the entire grid.
54245          * @param {Roo.EventObject} e
54246          */
54247         "mouseup" : true,
54248         /**
54249          * @event mouseover
54250          * The raw mouseover event for the entire grid.
54251          * @param {Roo.EventObject} e
54252          */
54253         "mouseover" : true,
54254         /**
54255          * @event mouseout
54256          * The raw mouseout event for the entire grid.
54257          * @param {Roo.EventObject} e
54258          */
54259         "mouseout" : true,
54260         /**
54261          * @event keypress
54262          * The raw keypress event for the entire grid.
54263          * @param {Roo.EventObject} e
54264          */
54265         "keypress" : true,
54266         /**
54267          * @event keydown
54268          * The raw keydown event for the entire grid.
54269          * @param {Roo.EventObject} e
54270          */
54271         "keydown" : true,
54272
54273         // custom events
54274
54275         /**
54276          * @event cellclick
54277          * Fires when a cell is clicked
54278          * @param {Grid} this
54279          * @param {Number} rowIndex
54280          * @param {Number} columnIndex
54281          * @param {Roo.EventObject} e
54282          */
54283         "cellclick" : true,
54284         /**
54285          * @event celldblclick
54286          * Fires when a cell is double clicked
54287          * @param {Grid} this
54288          * @param {Number} rowIndex
54289          * @param {Number} columnIndex
54290          * @param {Roo.EventObject} e
54291          */
54292         "celldblclick" : true,
54293         /**
54294          * @event rowclick
54295          * Fires when a row is clicked
54296          * @param {Grid} this
54297          * @param {Number} rowIndex
54298          * @param {Roo.EventObject} e
54299          */
54300         "rowclick" : true,
54301         /**
54302          * @event rowdblclick
54303          * Fires when a row is double clicked
54304          * @param {Grid} this
54305          * @param {Number} rowIndex
54306          * @param {Roo.EventObject} e
54307          */
54308         "rowdblclick" : true,
54309         /**
54310          * @event headerclick
54311          * Fires when a header is clicked
54312          * @param {Grid} this
54313          * @param {Number} columnIndex
54314          * @param {Roo.EventObject} e
54315          */
54316         "headerclick" : true,
54317         /**
54318          * @event headerdblclick
54319          * Fires when a header cell is double clicked
54320          * @param {Grid} this
54321          * @param {Number} columnIndex
54322          * @param {Roo.EventObject} e
54323          */
54324         "headerdblclick" : true,
54325         /**
54326          * @event rowcontextmenu
54327          * Fires when a row is right clicked
54328          * @param {Grid} this
54329          * @param {Number} rowIndex
54330          * @param {Roo.EventObject} e
54331          */
54332         "rowcontextmenu" : true,
54333         /**
54334          * @event cellcontextmenu
54335          * Fires when a cell is right clicked
54336          * @param {Grid} this
54337          * @param {Number} rowIndex
54338          * @param {Number} cellIndex
54339          * @param {Roo.EventObject} e
54340          */
54341          "cellcontextmenu" : true,
54342         /**
54343          * @event headercontextmenu
54344          * Fires when a header is right clicked
54345          * @param {Grid} this
54346          * @param {Number} columnIndex
54347          * @param {Roo.EventObject} e
54348          */
54349         "headercontextmenu" : true,
54350         /**
54351          * @event bodyscroll
54352          * Fires when the body element is scrolled
54353          * @param {Number} scrollLeft
54354          * @param {Number} scrollTop
54355          */
54356         "bodyscroll" : true,
54357         /**
54358          * @event columnresize
54359          * Fires when the user resizes a column
54360          * @param {Number} columnIndex
54361          * @param {Number} newSize
54362          */
54363         "columnresize" : true,
54364         /**
54365          * @event columnmove
54366          * Fires when the user moves a column
54367          * @param {Number} oldIndex
54368          * @param {Number} newIndex
54369          */
54370         "columnmove" : true,
54371         /**
54372          * @event startdrag
54373          * Fires when row(s) start being dragged
54374          * @param {Grid} this
54375          * @param {Roo.GridDD} dd The drag drop object
54376          * @param {event} e The raw browser event
54377          */
54378         "startdrag" : true,
54379         /**
54380          * @event enddrag
54381          * Fires when a drag operation is complete
54382          * @param {Grid} this
54383          * @param {Roo.GridDD} dd The drag drop object
54384          * @param {event} e The raw browser event
54385          */
54386         "enddrag" : true,
54387         /**
54388          * @event dragdrop
54389          * Fires when dragged row(s) are dropped on a valid DD target
54390          * @param {Grid} this
54391          * @param {Roo.GridDD} dd The drag drop object
54392          * @param {String} targetId The target drag drop object
54393          * @param {event} e The raw browser event
54394          */
54395         "dragdrop" : true,
54396         /**
54397          * @event dragover
54398          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54399          * @param {Grid} this
54400          * @param {Roo.GridDD} dd The drag drop object
54401          * @param {String} targetId The target drag drop object
54402          * @param {event} e The raw browser event
54403          */
54404         "dragover" : true,
54405         /**
54406          * @event dragenter
54407          *  Fires when the dragged row(s) first cross another DD target while being dragged
54408          * @param {Grid} this
54409          * @param {Roo.GridDD} dd The drag drop object
54410          * @param {String} targetId The target drag drop object
54411          * @param {event} e The raw browser event
54412          */
54413         "dragenter" : true,
54414         /**
54415          * @event dragout
54416          * Fires when the dragged row(s) leave another DD target while being dragged
54417          * @param {Grid} this
54418          * @param {Roo.GridDD} dd The drag drop object
54419          * @param {String} targetId The target drag drop object
54420          * @param {event} e The raw browser event
54421          */
54422         "dragout" : true,
54423         /**
54424          * @event rowclass
54425          * Fires when a row is rendered, so you can change add a style to it.
54426          * @param {GridView} gridview   The grid view
54427          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54428          */
54429         'rowclass' : true,
54430
54431         /**
54432          * @event render
54433          * Fires when the grid is rendered
54434          * @param {Grid} grid
54435          */
54436         'render' : true
54437     });
54438
54439     Roo.grid.Grid.superclass.constructor.call(this);
54440 };
54441 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54442     
54443     /**
54444      * @cfg {String} ddGroup - drag drop group.
54445      */
54446
54447     /**
54448      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54449      */
54450     minColumnWidth : 25,
54451
54452     /**
54453      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54454      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54455      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54456      */
54457     autoSizeColumns : false,
54458
54459     /**
54460      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54461      */
54462     autoSizeHeaders : true,
54463
54464     /**
54465      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54466      */
54467     monitorWindowResize : true,
54468
54469     /**
54470      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54471      * rows measured to get a columns size. Default is 0 (all rows).
54472      */
54473     maxRowsToMeasure : 0,
54474
54475     /**
54476      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54477      */
54478     trackMouseOver : true,
54479
54480     /**
54481     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54482     */
54483     
54484     /**
54485     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54486     */
54487     enableDragDrop : false,
54488     
54489     /**
54490     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54491     */
54492     enableColumnMove : true,
54493     
54494     /**
54495     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54496     */
54497     enableColumnHide : true,
54498     
54499     /**
54500     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54501     */
54502     enableRowHeightSync : false,
54503     
54504     /**
54505     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54506     */
54507     stripeRows : true,
54508     
54509     /**
54510     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54511     */
54512     autoHeight : false,
54513
54514     /**
54515      * @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.
54516      */
54517     autoExpandColumn : false,
54518
54519     /**
54520     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54521     * Default is 50.
54522     */
54523     autoExpandMin : 50,
54524
54525     /**
54526     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54527     */
54528     autoExpandMax : 1000,
54529
54530     /**
54531     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54532     */
54533     view : null,
54534
54535     /**
54536     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54537     */
54538     loadMask : false,
54539     /**
54540     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54541     */
54542     dropTarget: false,
54543     
54544    
54545     
54546     // private
54547     rendered : false,
54548
54549     /**
54550     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54551     * of a fixed width. Default is false.
54552     */
54553     /**
54554     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54555     */
54556     /**
54557      * Called once after all setup has been completed and the grid is ready to be rendered.
54558      * @return {Roo.grid.Grid} this
54559      */
54560     render : function()
54561     {
54562         var c = this.container;
54563         // try to detect autoHeight/width mode
54564         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54565             this.autoHeight = true;
54566         }
54567         var view = this.getView();
54568         view.init(this);
54569
54570         c.on("click", this.onClick, this);
54571         c.on("dblclick", this.onDblClick, this);
54572         c.on("contextmenu", this.onContextMenu, this);
54573         c.on("keydown", this.onKeyDown, this);
54574         if (Roo.isTouch) {
54575             c.on("touchstart", this.onTouchStart, this);
54576         }
54577
54578         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54579
54580         this.getSelectionModel().init(this);
54581
54582         view.render();
54583
54584         if(this.loadMask){
54585             this.loadMask = new Roo.LoadMask(this.container,
54586                     Roo.apply({store:this.dataSource}, this.loadMask));
54587         }
54588         
54589         
54590         if (this.toolbar && this.toolbar.xtype) {
54591             this.toolbar.container = this.getView().getHeaderPanel(true);
54592             this.toolbar = new Roo.Toolbar(this.toolbar);
54593         }
54594         if (this.footer && this.footer.xtype) {
54595             this.footer.dataSource = this.getDataSource();
54596             this.footer.container = this.getView().getFooterPanel(true);
54597             this.footer = Roo.factory(this.footer, Roo);
54598         }
54599         if (this.dropTarget && this.dropTarget.xtype) {
54600             delete this.dropTarget.xtype;
54601             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54602         }
54603         
54604         
54605         this.rendered = true;
54606         this.fireEvent('render', this);
54607         return this;
54608     },
54609
54610         /**
54611          * Reconfigures the grid to use a different Store and Column Model.
54612          * The View will be bound to the new objects and refreshed.
54613          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54614          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54615          */
54616     reconfigure : function(dataSource, colModel){
54617         if(this.loadMask){
54618             this.loadMask.destroy();
54619             this.loadMask = new Roo.LoadMask(this.container,
54620                     Roo.apply({store:dataSource}, this.loadMask));
54621         }
54622         this.view.bind(dataSource, colModel);
54623         this.dataSource = dataSource;
54624         this.colModel = colModel;
54625         this.view.refresh(true);
54626     },
54627
54628     // private
54629     onKeyDown : function(e){
54630         this.fireEvent("keydown", e);
54631     },
54632
54633     /**
54634      * Destroy this grid.
54635      * @param {Boolean} removeEl True to remove the element
54636      */
54637     destroy : function(removeEl, keepListeners){
54638         if(this.loadMask){
54639             this.loadMask.destroy();
54640         }
54641         var c = this.container;
54642         c.removeAllListeners();
54643         this.view.destroy();
54644         this.colModel.purgeListeners();
54645         if(!keepListeners){
54646             this.purgeListeners();
54647         }
54648         c.update("");
54649         if(removeEl === true){
54650             c.remove();
54651         }
54652     },
54653
54654     // private
54655     processEvent : function(name, e){
54656         // does this fire select???
54657         //Roo.log('grid:processEvent '  + name);
54658         
54659         if (name != 'touchstart' ) {
54660             this.fireEvent(name, e);    
54661         }
54662         
54663         var t = e.getTarget();
54664         var v = this.view;
54665         var header = v.findHeaderIndex(t);
54666         if(header !== false){
54667             var ename = name == 'touchstart' ? 'click' : name;
54668              
54669             this.fireEvent("header" + ename, this, header, e);
54670         }else{
54671             var row = v.findRowIndex(t);
54672             var cell = v.findCellIndex(t);
54673             if (name == 'touchstart') {
54674                 // first touch is always a click.
54675                 // hopefull this happens after selection is updated.?
54676                 name = false;
54677                 
54678                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54679                     var cs = this.selModel.getSelectedCell();
54680                     if (row == cs[0] && cell == cs[1]){
54681                         name = 'dblclick';
54682                     }
54683                 }
54684                 if (typeof(this.selModel.getSelections) != 'undefined') {
54685                     var cs = this.selModel.getSelections();
54686                     var ds = this.dataSource;
54687                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54688                         name = 'dblclick';
54689                     }
54690                 }
54691                 if (!name) {
54692                     return;
54693                 }
54694             }
54695             
54696             
54697             if(row !== false){
54698                 this.fireEvent("row" + name, this, row, e);
54699                 if(cell !== false){
54700                     this.fireEvent("cell" + name, this, row, cell, e);
54701                 }
54702             }
54703         }
54704     },
54705
54706     // private
54707     onClick : function(e){
54708         this.processEvent("click", e);
54709     },
54710    // private
54711     onTouchStart : function(e){
54712         this.processEvent("touchstart", e);
54713     },
54714
54715     // private
54716     onContextMenu : function(e, t){
54717         this.processEvent("contextmenu", e);
54718     },
54719
54720     // private
54721     onDblClick : function(e){
54722         this.processEvent("dblclick", e);
54723     },
54724
54725     // private
54726     walkCells : function(row, col, step, fn, scope){
54727         var cm = this.colModel, clen = cm.getColumnCount();
54728         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54729         if(step < 0){
54730             if(col < 0){
54731                 row--;
54732                 first = false;
54733             }
54734             while(row >= 0){
54735                 if(!first){
54736                     col = clen-1;
54737                 }
54738                 first = false;
54739                 while(col >= 0){
54740                     if(fn.call(scope || this, row, col, cm) === true){
54741                         return [row, col];
54742                     }
54743                     col--;
54744                 }
54745                 row--;
54746             }
54747         } else {
54748             if(col >= clen){
54749                 row++;
54750                 first = false;
54751             }
54752             while(row < rlen){
54753                 if(!first){
54754                     col = 0;
54755                 }
54756                 first = false;
54757                 while(col < clen){
54758                     if(fn.call(scope || this, row, col, cm) === true){
54759                         return [row, col];
54760                     }
54761                     col++;
54762                 }
54763                 row++;
54764             }
54765         }
54766         return null;
54767     },
54768
54769     // private
54770     getSelections : function(){
54771         return this.selModel.getSelections();
54772     },
54773
54774     /**
54775      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54776      * but if manual update is required this method will initiate it.
54777      */
54778     autoSize : function(){
54779         if(this.rendered){
54780             this.view.layout();
54781             if(this.view.adjustForScroll){
54782                 this.view.adjustForScroll();
54783             }
54784         }
54785     },
54786
54787     /**
54788      * Returns the grid's underlying element.
54789      * @return {Element} The element
54790      */
54791     getGridEl : function(){
54792         return this.container;
54793     },
54794
54795     // private for compatibility, overridden by editor grid
54796     stopEditing : function(){},
54797
54798     /**
54799      * Returns the grid's SelectionModel.
54800      * @return {SelectionModel}
54801      */
54802     getSelectionModel : function(){
54803         if(!this.selModel){
54804             this.selModel = new Roo.grid.RowSelectionModel();
54805         }
54806         return this.selModel;
54807     },
54808
54809     /**
54810      * Returns the grid's DataSource.
54811      * @return {DataSource}
54812      */
54813     getDataSource : function(){
54814         return this.dataSource;
54815     },
54816
54817     /**
54818      * Returns the grid's ColumnModel.
54819      * @return {ColumnModel}
54820      */
54821     getColumnModel : function(){
54822         return this.colModel;
54823     },
54824
54825     /**
54826      * Returns the grid's GridView object.
54827      * @return {GridView}
54828      */
54829     getView : function(){
54830         if(!this.view){
54831             this.view = new Roo.grid.GridView(this.viewConfig);
54832         }
54833         return this.view;
54834     },
54835     /**
54836      * Called to get grid's drag proxy text, by default returns this.ddText.
54837      * @return {String}
54838      */
54839     getDragDropText : function(){
54840         var count = this.selModel.getCount();
54841         return String.format(this.ddText, count, count == 1 ? '' : 's');
54842     }
54843 });
54844 /**
54845  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54846  * %0 is replaced with the number of selected rows.
54847  * @type String
54848  */
54849 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54850  * Based on:
54851  * Ext JS Library 1.1.1
54852  * Copyright(c) 2006-2007, Ext JS, LLC.
54853  *
54854  * Originally Released Under LGPL - original licence link has changed is not relivant.
54855  *
54856  * Fork - LGPL
54857  * <script type="text/javascript">
54858  */
54859  
54860 Roo.grid.AbstractGridView = function(){
54861         this.grid = null;
54862         
54863         this.events = {
54864             "beforerowremoved" : true,
54865             "beforerowsinserted" : true,
54866             "beforerefresh" : true,
54867             "rowremoved" : true,
54868             "rowsinserted" : true,
54869             "rowupdated" : true,
54870             "refresh" : true
54871         };
54872     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54873 };
54874
54875 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54876     rowClass : "x-grid-row",
54877     cellClass : "x-grid-cell",
54878     tdClass : "x-grid-td",
54879     hdClass : "x-grid-hd",
54880     splitClass : "x-grid-hd-split",
54881     
54882     init: function(grid){
54883         this.grid = grid;
54884                 var cid = this.grid.getGridEl().id;
54885         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54886         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54887         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54888         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54889         },
54890         
54891     getColumnRenderers : function(){
54892         var renderers = [];
54893         var cm = this.grid.colModel;
54894         var colCount = cm.getColumnCount();
54895         for(var i = 0; i < colCount; i++){
54896             renderers[i] = cm.getRenderer(i);
54897         }
54898         return renderers;
54899     },
54900     
54901     getColumnIds : function(){
54902         var ids = [];
54903         var cm = this.grid.colModel;
54904         var colCount = cm.getColumnCount();
54905         for(var i = 0; i < colCount; i++){
54906             ids[i] = cm.getColumnId(i);
54907         }
54908         return ids;
54909     },
54910     
54911     getDataIndexes : function(){
54912         if(!this.indexMap){
54913             this.indexMap = this.buildIndexMap();
54914         }
54915         return this.indexMap.colToData;
54916     },
54917     
54918     getColumnIndexByDataIndex : function(dataIndex){
54919         if(!this.indexMap){
54920             this.indexMap = this.buildIndexMap();
54921         }
54922         return this.indexMap.dataToCol[dataIndex];
54923     },
54924     
54925     /**
54926      * Set a css style for a column dynamically. 
54927      * @param {Number} colIndex The index of the column
54928      * @param {String} name The css property name
54929      * @param {String} value The css value
54930      */
54931     setCSSStyle : function(colIndex, name, value){
54932         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54933         Roo.util.CSS.updateRule(selector, name, value);
54934     },
54935     
54936     generateRules : function(cm){
54937         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54938         Roo.util.CSS.removeStyleSheet(rulesId);
54939         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54940             var cid = cm.getColumnId(i);
54941             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54942                          this.tdSelector, cid, " {\n}\n",
54943                          this.hdSelector, cid, " {\n}\n",
54944                          this.splitSelector, cid, " {\n}\n");
54945         }
54946         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54947     }
54948 });/*
54949  * Based on:
54950  * Ext JS Library 1.1.1
54951  * Copyright(c) 2006-2007, Ext JS, LLC.
54952  *
54953  * Originally Released Under LGPL - original licence link has changed is not relivant.
54954  *
54955  * Fork - LGPL
54956  * <script type="text/javascript">
54957  */
54958
54959 // private
54960 // This is a support class used internally by the Grid components
54961 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54962     this.grid = grid;
54963     this.view = grid.getView();
54964     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54965     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54966     if(hd2){
54967         this.setHandleElId(Roo.id(hd));
54968         this.setOuterHandleElId(Roo.id(hd2));
54969     }
54970     this.scroll = false;
54971 };
54972 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54973     maxDragWidth: 120,
54974     getDragData : function(e){
54975         var t = Roo.lib.Event.getTarget(e);
54976         var h = this.view.findHeaderCell(t);
54977         if(h){
54978             return {ddel: h.firstChild, header:h};
54979         }
54980         return false;
54981     },
54982
54983     onInitDrag : function(e){
54984         this.view.headersDisabled = true;
54985         var clone = this.dragData.ddel.cloneNode(true);
54986         clone.id = Roo.id();
54987         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54988         this.proxy.update(clone);
54989         return true;
54990     },
54991
54992     afterValidDrop : function(){
54993         var v = this.view;
54994         setTimeout(function(){
54995             v.headersDisabled = false;
54996         }, 50);
54997     },
54998
54999     afterInvalidDrop : function(){
55000         var v = this.view;
55001         setTimeout(function(){
55002             v.headersDisabled = false;
55003         }, 50);
55004     }
55005 });
55006 /*
55007  * Based on:
55008  * Ext JS Library 1.1.1
55009  * Copyright(c) 2006-2007, Ext JS, LLC.
55010  *
55011  * Originally Released Under LGPL - original licence link has changed is not relivant.
55012  *
55013  * Fork - LGPL
55014  * <script type="text/javascript">
55015  */
55016 // private
55017 // This is a support class used internally by the Grid components
55018 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55019     this.grid = grid;
55020     this.view = grid.getView();
55021     // split the proxies so they don't interfere with mouse events
55022     this.proxyTop = Roo.DomHelper.append(document.body, {
55023         cls:"col-move-top", html:"&#160;"
55024     }, true);
55025     this.proxyBottom = Roo.DomHelper.append(document.body, {
55026         cls:"col-move-bottom", html:"&#160;"
55027     }, true);
55028     this.proxyTop.hide = this.proxyBottom.hide = function(){
55029         this.setLeftTop(-100,-100);
55030         this.setStyle("visibility", "hidden");
55031     };
55032     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55033     // temporarily disabled
55034     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55035     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55036 };
55037 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55038     proxyOffsets : [-4, -9],
55039     fly: Roo.Element.fly,
55040
55041     getTargetFromEvent : function(e){
55042         var t = Roo.lib.Event.getTarget(e);
55043         var cindex = this.view.findCellIndex(t);
55044         if(cindex !== false){
55045             return this.view.getHeaderCell(cindex);
55046         }
55047         return null;
55048     },
55049
55050     nextVisible : function(h){
55051         var v = this.view, cm = this.grid.colModel;
55052         h = h.nextSibling;
55053         while(h){
55054             if(!cm.isHidden(v.getCellIndex(h))){
55055                 return h;
55056             }
55057             h = h.nextSibling;
55058         }
55059         return null;
55060     },
55061
55062     prevVisible : function(h){
55063         var v = this.view, cm = this.grid.colModel;
55064         h = h.prevSibling;
55065         while(h){
55066             if(!cm.isHidden(v.getCellIndex(h))){
55067                 return h;
55068             }
55069             h = h.prevSibling;
55070         }
55071         return null;
55072     },
55073
55074     positionIndicator : function(h, n, e){
55075         var x = Roo.lib.Event.getPageX(e);
55076         var r = Roo.lib.Dom.getRegion(n.firstChild);
55077         var px, pt, py = r.top + this.proxyOffsets[1];
55078         if((r.right - x) <= (r.right-r.left)/2){
55079             px = r.right+this.view.borderWidth;
55080             pt = "after";
55081         }else{
55082             px = r.left;
55083             pt = "before";
55084         }
55085         var oldIndex = this.view.getCellIndex(h);
55086         var newIndex = this.view.getCellIndex(n);
55087
55088         if(this.grid.colModel.isFixed(newIndex)){
55089             return false;
55090         }
55091
55092         var locked = this.grid.colModel.isLocked(newIndex);
55093
55094         if(pt == "after"){
55095             newIndex++;
55096         }
55097         if(oldIndex < newIndex){
55098             newIndex--;
55099         }
55100         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55101             return false;
55102         }
55103         px +=  this.proxyOffsets[0];
55104         this.proxyTop.setLeftTop(px, py);
55105         this.proxyTop.show();
55106         if(!this.bottomOffset){
55107             this.bottomOffset = this.view.mainHd.getHeight();
55108         }
55109         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55110         this.proxyBottom.show();
55111         return pt;
55112     },
55113
55114     onNodeEnter : function(n, dd, e, data){
55115         if(data.header != n){
55116             this.positionIndicator(data.header, n, e);
55117         }
55118     },
55119
55120     onNodeOver : function(n, dd, e, data){
55121         var result = false;
55122         if(data.header != n){
55123             result = this.positionIndicator(data.header, n, e);
55124         }
55125         if(!result){
55126             this.proxyTop.hide();
55127             this.proxyBottom.hide();
55128         }
55129         return result ? this.dropAllowed : this.dropNotAllowed;
55130     },
55131
55132     onNodeOut : function(n, dd, e, data){
55133         this.proxyTop.hide();
55134         this.proxyBottom.hide();
55135     },
55136
55137     onNodeDrop : function(n, dd, e, data){
55138         var h = data.header;
55139         if(h != n){
55140             var cm = this.grid.colModel;
55141             var x = Roo.lib.Event.getPageX(e);
55142             var r = Roo.lib.Dom.getRegion(n.firstChild);
55143             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55144             var oldIndex = this.view.getCellIndex(h);
55145             var newIndex = this.view.getCellIndex(n);
55146             var locked = cm.isLocked(newIndex);
55147             if(pt == "after"){
55148                 newIndex++;
55149             }
55150             if(oldIndex < newIndex){
55151                 newIndex--;
55152             }
55153             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55154                 return false;
55155             }
55156             cm.setLocked(oldIndex, locked, true);
55157             cm.moveColumn(oldIndex, newIndex);
55158             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55159             return true;
55160         }
55161         return false;
55162     }
55163 });
55164 /*
55165  * Based on:
55166  * Ext JS Library 1.1.1
55167  * Copyright(c) 2006-2007, Ext JS, LLC.
55168  *
55169  * Originally Released Under LGPL - original licence link has changed is not relivant.
55170  *
55171  * Fork - LGPL
55172  * <script type="text/javascript">
55173  */
55174   
55175 /**
55176  * @class Roo.grid.GridView
55177  * @extends Roo.util.Observable
55178  *
55179  * @constructor
55180  * @param {Object} config
55181  */
55182 Roo.grid.GridView = function(config){
55183     Roo.grid.GridView.superclass.constructor.call(this);
55184     this.el = null;
55185
55186     Roo.apply(this, config);
55187 };
55188
55189 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55190
55191     unselectable :  'unselectable="on"',
55192     unselectableCls :  'x-unselectable',
55193     
55194     
55195     rowClass : "x-grid-row",
55196
55197     cellClass : "x-grid-col",
55198
55199     tdClass : "x-grid-td",
55200
55201     hdClass : "x-grid-hd",
55202
55203     splitClass : "x-grid-split",
55204
55205     sortClasses : ["sort-asc", "sort-desc"],
55206
55207     enableMoveAnim : false,
55208
55209     hlColor: "C3DAF9",
55210
55211     dh : Roo.DomHelper,
55212
55213     fly : Roo.Element.fly,
55214
55215     css : Roo.util.CSS,
55216
55217     borderWidth: 1,
55218
55219     splitOffset: 3,
55220
55221     scrollIncrement : 22,
55222
55223     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55224
55225     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55226
55227     bind : function(ds, cm){
55228         if(this.ds){
55229             this.ds.un("load", this.onLoad, this);
55230             this.ds.un("datachanged", this.onDataChange, this);
55231             this.ds.un("add", this.onAdd, this);
55232             this.ds.un("remove", this.onRemove, this);
55233             this.ds.un("update", this.onUpdate, this);
55234             this.ds.un("clear", this.onClear, this);
55235         }
55236         if(ds){
55237             ds.on("load", this.onLoad, this);
55238             ds.on("datachanged", this.onDataChange, this);
55239             ds.on("add", this.onAdd, this);
55240             ds.on("remove", this.onRemove, this);
55241             ds.on("update", this.onUpdate, this);
55242             ds.on("clear", this.onClear, this);
55243         }
55244         this.ds = ds;
55245
55246         if(this.cm){
55247             this.cm.un("widthchange", this.onColWidthChange, this);
55248             this.cm.un("headerchange", this.onHeaderChange, this);
55249             this.cm.un("hiddenchange", this.onHiddenChange, this);
55250             this.cm.un("columnmoved", this.onColumnMove, this);
55251             this.cm.un("columnlockchange", this.onColumnLock, this);
55252         }
55253         if(cm){
55254             this.generateRules(cm);
55255             cm.on("widthchange", this.onColWidthChange, this);
55256             cm.on("headerchange", this.onHeaderChange, this);
55257             cm.on("hiddenchange", this.onHiddenChange, this);
55258             cm.on("columnmoved", this.onColumnMove, this);
55259             cm.on("columnlockchange", this.onColumnLock, this);
55260         }
55261         this.cm = cm;
55262     },
55263
55264     init: function(grid){
55265         Roo.grid.GridView.superclass.init.call(this, grid);
55266
55267         this.bind(grid.dataSource, grid.colModel);
55268
55269         grid.on("headerclick", this.handleHeaderClick, this);
55270
55271         if(grid.trackMouseOver){
55272             grid.on("mouseover", this.onRowOver, this);
55273             grid.on("mouseout", this.onRowOut, this);
55274         }
55275         grid.cancelTextSelection = function(){};
55276         this.gridId = grid.id;
55277
55278         var tpls = this.templates || {};
55279
55280         if(!tpls.master){
55281             tpls.master = new Roo.Template(
55282                '<div class="x-grid" hidefocus="true">',
55283                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55284                   '<div class="x-grid-topbar"></div>',
55285                   '<div class="x-grid-scroller"><div></div></div>',
55286                   '<div class="x-grid-locked">',
55287                       '<div class="x-grid-header">{lockedHeader}</div>',
55288                       '<div class="x-grid-body">{lockedBody}</div>',
55289                   "</div>",
55290                   '<div class="x-grid-viewport">',
55291                       '<div class="x-grid-header">{header}</div>',
55292                       '<div class="x-grid-body">{body}</div>',
55293                   "</div>",
55294                   '<div class="x-grid-bottombar"></div>',
55295                  
55296                   '<div class="x-grid-resize-proxy">&#160;</div>',
55297                "</div>"
55298             );
55299             tpls.master.disableformats = true;
55300         }
55301
55302         if(!tpls.header){
55303             tpls.header = new Roo.Template(
55304                '<table border="0" cellspacing="0" cellpadding="0">',
55305                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55306                "</table>{splits}"
55307             );
55308             tpls.header.disableformats = true;
55309         }
55310         tpls.header.compile();
55311
55312         if(!tpls.hcell){
55313             tpls.hcell = new Roo.Template(
55314                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55315                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55316                 "</div></td>"
55317              );
55318              tpls.hcell.disableFormats = true;
55319         }
55320         tpls.hcell.compile();
55321
55322         if(!tpls.hsplit){
55323             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55324                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55325             tpls.hsplit.disableFormats = true;
55326         }
55327         tpls.hsplit.compile();
55328
55329         if(!tpls.body){
55330             tpls.body = new Roo.Template(
55331                '<table border="0" cellspacing="0" cellpadding="0">',
55332                "<tbody>{rows}</tbody>",
55333                "</table>"
55334             );
55335             tpls.body.disableFormats = true;
55336         }
55337         tpls.body.compile();
55338
55339         if(!tpls.row){
55340             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55341             tpls.row.disableFormats = true;
55342         }
55343         tpls.row.compile();
55344
55345         if(!tpls.cell){
55346             tpls.cell = new Roo.Template(
55347                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55348                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55349                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55350                 "</td>"
55351             );
55352             tpls.cell.disableFormats = true;
55353         }
55354         tpls.cell.compile();
55355
55356         this.templates = tpls;
55357     },
55358
55359     // remap these for backwards compat
55360     onColWidthChange : function(){
55361         this.updateColumns.apply(this, arguments);
55362     },
55363     onHeaderChange : function(){
55364         this.updateHeaders.apply(this, arguments);
55365     }, 
55366     onHiddenChange : function(){
55367         this.handleHiddenChange.apply(this, arguments);
55368     },
55369     onColumnMove : function(){
55370         this.handleColumnMove.apply(this, arguments);
55371     },
55372     onColumnLock : function(){
55373         this.handleLockChange.apply(this, arguments);
55374     },
55375
55376     onDataChange : function(){
55377         this.refresh();
55378         this.updateHeaderSortState();
55379     },
55380
55381     onClear : function(){
55382         this.refresh();
55383     },
55384
55385     onUpdate : function(ds, record){
55386         this.refreshRow(record);
55387     },
55388
55389     refreshRow : function(record){
55390         var ds = this.ds, index;
55391         if(typeof record == 'number'){
55392             index = record;
55393             record = ds.getAt(index);
55394         }else{
55395             index = ds.indexOf(record);
55396         }
55397         this.insertRows(ds, index, index, true);
55398         this.onRemove(ds, record, index+1, true);
55399         this.syncRowHeights(index, index);
55400         this.layout();
55401         this.fireEvent("rowupdated", this, index, record);
55402     },
55403
55404     onAdd : function(ds, records, index){
55405         this.insertRows(ds, index, index + (records.length-1));
55406     },
55407
55408     onRemove : function(ds, record, index, isUpdate){
55409         if(isUpdate !== true){
55410             this.fireEvent("beforerowremoved", this, index, record);
55411         }
55412         var bt = this.getBodyTable(), lt = this.getLockedTable();
55413         if(bt.rows[index]){
55414             bt.firstChild.removeChild(bt.rows[index]);
55415         }
55416         if(lt.rows[index]){
55417             lt.firstChild.removeChild(lt.rows[index]);
55418         }
55419         if(isUpdate !== true){
55420             this.stripeRows(index);
55421             this.syncRowHeights(index, index);
55422             this.layout();
55423             this.fireEvent("rowremoved", this, index, record);
55424         }
55425     },
55426
55427     onLoad : function(){
55428         this.scrollToTop();
55429     },
55430
55431     /**
55432      * Scrolls the grid to the top
55433      */
55434     scrollToTop : function(){
55435         if(this.scroller){
55436             this.scroller.dom.scrollTop = 0;
55437             this.syncScroll();
55438         }
55439     },
55440
55441     /**
55442      * Gets a panel in the header of the grid that can be used for toolbars etc.
55443      * After modifying the contents of this panel a call to grid.autoSize() may be
55444      * required to register any changes in size.
55445      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55446      * @return Roo.Element
55447      */
55448     getHeaderPanel : function(doShow){
55449         if(doShow){
55450             this.headerPanel.show();
55451         }
55452         return this.headerPanel;
55453     },
55454
55455     /**
55456      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55457      * After modifying the contents of this panel a call to grid.autoSize() may be
55458      * required to register any changes in size.
55459      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55460      * @return Roo.Element
55461      */
55462     getFooterPanel : function(doShow){
55463         if(doShow){
55464             this.footerPanel.show();
55465         }
55466         return this.footerPanel;
55467     },
55468
55469     initElements : function(){
55470         var E = Roo.Element;
55471         var el = this.grid.getGridEl().dom.firstChild;
55472         var cs = el.childNodes;
55473
55474         this.el = new E(el);
55475         
55476          this.focusEl = new E(el.firstChild);
55477         this.focusEl.swallowEvent("click", true);
55478         
55479         this.headerPanel = new E(cs[1]);
55480         this.headerPanel.enableDisplayMode("block");
55481
55482         this.scroller = new E(cs[2]);
55483         this.scrollSizer = new E(this.scroller.dom.firstChild);
55484
55485         this.lockedWrap = new E(cs[3]);
55486         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55487         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55488
55489         this.mainWrap = new E(cs[4]);
55490         this.mainHd = new E(this.mainWrap.dom.firstChild);
55491         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55492
55493         this.footerPanel = new E(cs[5]);
55494         this.footerPanel.enableDisplayMode("block");
55495
55496         this.resizeProxy = new E(cs[6]);
55497
55498         this.headerSelector = String.format(
55499            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55500            this.lockedHd.id, this.mainHd.id
55501         );
55502
55503         this.splitterSelector = String.format(
55504            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55505            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55506         );
55507     },
55508     idToCssName : function(s)
55509     {
55510         return s.replace(/[^a-z0-9]+/ig, '-');
55511     },
55512
55513     getHeaderCell : function(index){
55514         return Roo.DomQuery.select(this.headerSelector)[index];
55515     },
55516
55517     getHeaderCellMeasure : function(index){
55518         return this.getHeaderCell(index).firstChild;
55519     },
55520
55521     getHeaderCellText : function(index){
55522         return this.getHeaderCell(index).firstChild.firstChild;
55523     },
55524
55525     getLockedTable : function(){
55526         return this.lockedBody.dom.firstChild;
55527     },
55528
55529     getBodyTable : function(){
55530         return this.mainBody.dom.firstChild;
55531     },
55532
55533     getLockedRow : function(index){
55534         return this.getLockedTable().rows[index];
55535     },
55536
55537     getRow : function(index){
55538         return this.getBodyTable().rows[index];
55539     },
55540
55541     getRowComposite : function(index){
55542         if(!this.rowEl){
55543             this.rowEl = new Roo.CompositeElementLite();
55544         }
55545         var els = [], lrow, mrow;
55546         if(lrow = this.getLockedRow(index)){
55547             els.push(lrow);
55548         }
55549         if(mrow = this.getRow(index)){
55550             els.push(mrow);
55551         }
55552         this.rowEl.elements = els;
55553         return this.rowEl;
55554     },
55555     /**
55556      * Gets the 'td' of the cell
55557      * 
55558      * @param {Integer} rowIndex row to select
55559      * @param {Integer} colIndex column to select
55560      * 
55561      * @return {Object} 
55562      */
55563     getCell : function(rowIndex, colIndex){
55564         var locked = this.cm.getLockedCount();
55565         var source;
55566         if(colIndex < locked){
55567             source = this.lockedBody.dom.firstChild;
55568         }else{
55569             source = this.mainBody.dom.firstChild;
55570             colIndex -= locked;
55571         }
55572         return source.rows[rowIndex].childNodes[colIndex];
55573     },
55574
55575     getCellText : function(rowIndex, colIndex){
55576         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55577     },
55578
55579     getCellBox : function(cell){
55580         var b = this.fly(cell).getBox();
55581         if(Roo.isOpera){ // opera fails to report the Y
55582             b.y = cell.offsetTop + this.mainBody.getY();
55583         }
55584         return b;
55585     },
55586
55587     getCellIndex : function(cell){
55588         var id = String(cell.className).match(this.cellRE);
55589         if(id){
55590             return parseInt(id[1], 10);
55591         }
55592         return 0;
55593     },
55594
55595     findHeaderIndex : function(n){
55596         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55597         return r ? this.getCellIndex(r) : false;
55598     },
55599
55600     findHeaderCell : function(n){
55601         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55602         return r ? r : false;
55603     },
55604
55605     findRowIndex : function(n){
55606         if(!n){
55607             return false;
55608         }
55609         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55610         return r ? r.rowIndex : false;
55611     },
55612
55613     findCellIndex : function(node){
55614         var stop = this.el.dom;
55615         while(node && node != stop){
55616             if(this.findRE.test(node.className)){
55617                 return this.getCellIndex(node);
55618             }
55619             node = node.parentNode;
55620         }
55621         return false;
55622     },
55623
55624     getColumnId : function(index){
55625         return this.cm.getColumnId(index);
55626     },
55627
55628     getSplitters : function()
55629     {
55630         if(this.splitterSelector){
55631            return Roo.DomQuery.select(this.splitterSelector);
55632         }else{
55633             return null;
55634       }
55635     },
55636
55637     getSplitter : function(index){
55638         return this.getSplitters()[index];
55639     },
55640
55641     onRowOver : function(e, t){
55642         var row;
55643         if((row = this.findRowIndex(t)) !== false){
55644             this.getRowComposite(row).addClass("x-grid-row-over");
55645         }
55646     },
55647
55648     onRowOut : function(e, t){
55649         var row;
55650         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55651             this.getRowComposite(row).removeClass("x-grid-row-over");
55652         }
55653     },
55654
55655     renderHeaders : function(){
55656         var cm = this.cm;
55657         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55658         var cb = [], lb = [], sb = [], lsb = [], p = {};
55659         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55660             p.cellId = "x-grid-hd-0-" + i;
55661             p.splitId = "x-grid-csplit-0-" + i;
55662             p.id = cm.getColumnId(i);
55663             p.value = cm.getColumnHeader(i) || "";
55664             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55665             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55666             if(!cm.isLocked(i)){
55667                 cb[cb.length] = ct.apply(p);
55668                 sb[sb.length] = st.apply(p);
55669             }else{
55670                 lb[lb.length] = ct.apply(p);
55671                 lsb[lsb.length] = st.apply(p);
55672             }
55673         }
55674         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55675                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55676     },
55677
55678     updateHeaders : function(){
55679         var html = this.renderHeaders();
55680         this.lockedHd.update(html[0]);
55681         this.mainHd.update(html[1]);
55682     },
55683
55684     /**
55685      * Focuses the specified row.
55686      * @param {Number} row The row index
55687      */
55688     focusRow : function(row)
55689     {
55690         //Roo.log('GridView.focusRow');
55691         var x = this.scroller.dom.scrollLeft;
55692         this.focusCell(row, 0, false);
55693         this.scroller.dom.scrollLeft = x;
55694     },
55695
55696     /**
55697      * Focuses the specified cell.
55698      * @param {Number} row The row index
55699      * @param {Number} col The column index
55700      * @param {Boolean} hscroll false to disable horizontal scrolling
55701      */
55702     focusCell : function(row, col, hscroll)
55703     {
55704         //Roo.log('GridView.focusCell');
55705         var el = this.ensureVisible(row, col, hscroll);
55706         this.focusEl.alignTo(el, "tl-tl");
55707         if(Roo.isGecko){
55708             this.focusEl.focus();
55709         }else{
55710             this.focusEl.focus.defer(1, this.focusEl);
55711         }
55712     },
55713
55714     /**
55715      * Scrolls the specified cell into view
55716      * @param {Number} row The row index
55717      * @param {Number} col The column index
55718      * @param {Boolean} hscroll false to disable horizontal scrolling
55719      */
55720     ensureVisible : function(row, col, hscroll)
55721     {
55722         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55723         //return null; //disable for testing.
55724         if(typeof row != "number"){
55725             row = row.rowIndex;
55726         }
55727         if(row < 0 && row >= this.ds.getCount()){
55728             return  null;
55729         }
55730         col = (col !== undefined ? col : 0);
55731         var cm = this.grid.colModel;
55732         while(cm.isHidden(col)){
55733             col++;
55734         }
55735
55736         var el = this.getCell(row, col);
55737         if(!el){
55738             return null;
55739         }
55740         var c = this.scroller.dom;
55741
55742         var ctop = parseInt(el.offsetTop, 10);
55743         var cleft = parseInt(el.offsetLeft, 10);
55744         var cbot = ctop + el.offsetHeight;
55745         var cright = cleft + el.offsetWidth;
55746         
55747         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55748         var stop = parseInt(c.scrollTop, 10);
55749         var sleft = parseInt(c.scrollLeft, 10);
55750         var sbot = stop + ch;
55751         var sright = sleft + c.clientWidth;
55752         /*
55753         Roo.log('GridView.ensureVisible:' +
55754                 ' ctop:' + ctop +
55755                 ' c.clientHeight:' + c.clientHeight +
55756                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55757                 ' stop:' + stop +
55758                 ' cbot:' + cbot +
55759                 ' sbot:' + sbot +
55760                 ' ch:' + ch  
55761                 );
55762         */
55763         if(ctop < stop){
55764              c.scrollTop = ctop;
55765             //Roo.log("set scrolltop to ctop DISABLE?");
55766         }else if(cbot > sbot){
55767             //Roo.log("set scrolltop to cbot-ch");
55768             c.scrollTop = cbot-ch;
55769         }
55770         
55771         if(hscroll !== false){
55772             if(cleft < sleft){
55773                 c.scrollLeft = cleft;
55774             }else if(cright > sright){
55775                 c.scrollLeft = cright-c.clientWidth;
55776             }
55777         }
55778          
55779         return el;
55780     },
55781
55782     updateColumns : function(){
55783         this.grid.stopEditing();
55784         var cm = this.grid.colModel, colIds = this.getColumnIds();
55785         //var totalWidth = cm.getTotalWidth();
55786         var pos = 0;
55787         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55788             //if(cm.isHidden(i)) continue;
55789             var w = cm.getColumnWidth(i);
55790             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55791             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55792         }
55793         this.updateSplitters();
55794     },
55795
55796     generateRules : function(cm){
55797         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55798         Roo.util.CSS.removeStyleSheet(rulesId);
55799         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55800             var cid = cm.getColumnId(i);
55801             var align = '';
55802             if(cm.config[i].align){
55803                 align = 'text-align:'+cm.config[i].align+';';
55804             }
55805             var hidden = '';
55806             if(cm.isHidden(i)){
55807                 hidden = 'display:none;';
55808             }
55809             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55810             ruleBuf.push(
55811                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55812                     this.hdSelector, cid, " {\n", align, width, "}\n",
55813                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55814                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55815         }
55816         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55817     },
55818
55819     updateSplitters : function(){
55820         var cm = this.cm, s = this.getSplitters();
55821         if(s){ // splitters not created yet
55822             var pos = 0, locked = true;
55823             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55824                 if(cm.isHidden(i)) {
55825                     continue;
55826                 }
55827                 var w = cm.getColumnWidth(i); // make sure it's a number
55828                 if(!cm.isLocked(i) && locked){
55829                     pos = 0;
55830                     locked = false;
55831                 }
55832                 pos += w;
55833                 s[i].style.left = (pos-this.splitOffset) + "px";
55834             }
55835         }
55836     },
55837
55838     handleHiddenChange : function(colModel, colIndex, hidden){
55839         if(hidden){
55840             this.hideColumn(colIndex);
55841         }else{
55842             this.unhideColumn(colIndex);
55843         }
55844     },
55845
55846     hideColumn : function(colIndex){
55847         var cid = this.getColumnId(colIndex);
55848         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55849         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55850         if(Roo.isSafari){
55851             this.updateHeaders();
55852         }
55853         this.updateSplitters();
55854         this.layout();
55855     },
55856
55857     unhideColumn : function(colIndex){
55858         var cid = this.getColumnId(colIndex);
55859         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55860         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55861
55862         if(Roo.isSafari){
55863             this.updateHeaders();
55864         }
55865         this.updateSplitters();
55866         this.layout();
55867     },
55868
55869     insertRows : function(dm, firstRow, lastRow, isUpdate){
55870         if(firstRow == 0 && lastRow == dm.getCount()-1){
55871             this.refresh();
55872         }else{
55873             if(!isUpdate){
55874                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55875             }
55876             var s = this.getScrollState();
55877             var markup = this.renderRows(firstRow, lastRow);
55878             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55879             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55880             this.restoreScroll(s);
55881             if(!isUpdate){
55882                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55883                 this.syncRowHeights(firstRow, lastRow);
55884                 this.stripeRows(firstRow);
55885                 this.layout();
55886             }
55887         }
55888     },
55889
55890     bufferRows : function(markup, target, index){
55891         var before = null, trows = target.rows, tbody = target.tBodies[0];
55892         if(index < trows.length){
55893             before = trows[index];
55894         }
55895         var b = document.createElement("div");
55896         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55897         var rows = b.firstChild.rows;
55898         for(var i = 0, len = rows.length; i < len; i++){
55899             if(before){
55900                 tbody.insertBefore(rows[0], before);
55901             }else{
55902                 tbody.appendChild(rows[0]);
55903             }
55904         }
55905         b.innerHTML = "";
55906         b = null;
55907     },
55908
55909     deleteRows : function(dm, firstRow, lastRow){
55910         if(dm.getRowCount()<1){
55911             this.fireEvent("beforerefresh", this);
55912             this.mainBody.update("");
55913             this.lockedBody.update("");
55914             this.fireEvent("refresh", this);
55915         }else{
55916             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55917             var bt = this.getBodyTable();
55918             var tbody = bt.firstChild;
55919             var rows = bt.rows;
55920             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55921                 tbody.removeChild(rows[firstRow]);
55922             }
55923             this.stripeRows(firstRow);
55924             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55925         }
55926     },
55927
55928     updateRows : function(dataSource, firstRow, lastRow){
55929         var s = this.getScrollState();
55930         this.refresh();
55931         this.restoreScroll(s);
55932     },
55933
55934     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55935         if(!noRefresh){
55936            this.refresh();
55937         }
55938         this.updateHeaderSortState();
55939     },
55940
55941     getScrollState : function(){
55942         
55943         var sb = this.scroller.dom;
55944         return {left: sb.scrollLeft, top: sb.scrollTop};
55945     },
55946
55947     stripeRows : function(startRow){
55948         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55949             return;
55950         }
55951         startRow = startRow || 0;
55952         var rows = this.getBodyTable().rows;
55953         var lrows = this.getLockedTable().rows;
55954         var cls = ' x-grid-row-alt ';
55955         for(var i = startRow, len = rows.length; i < len; i++){
55956             var row = rows[i], lrow = lrows[i];
55957             var isAlt = ((i+1) % 2 == 0);
55958             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55959             if(isAlt == hasAlt){
55960                 continue;
55961             }
55962             if(isAlt){
55963                 row.className += " x-grid-row-alt";
55964             }else{
55965                 row.className = row.className.replace("x-grid-row-alt", "");
55966             }
55967             if(lrow){
55968                 lrow.className = row.className;
55969             }
55970         }
55971     },
55972
55973     restoreScroll : function(state){
55974         //Roo.log('GridView.restoreScroll');
55975         var sb = this.scroller.dom;
55976         sb.scrollLeft = state.left;
55977         sb.scrollTop = state.top;
55978         this.syncScroll();
55979     },
55980
55981     syncScroll : function(){
55982         //Roo.log('GridView.syncScroll');
55983         var sb = this.scroller.dom;
55984         var sh = this.mainHd.dom;
55985         var bs = this.mainBody.dom;
55986         var lv = this.lockedBody.dom;
55987         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55988         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55989     },
55990
55991     handleScroll : function(e){
55992         this.syncScroll();
55993         var sb = this.scroller.dom;
55994         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55995         e.stopEvent();
55996     },
55997
55998     handleWheel : function(e){
55999         var d = e.getWheelDelta();
56000         this.scroller.dom.scrollTop -= d*22;
56001         // set this here to prevent jumpy scrolling on large tables
56002         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56003         e.stopEvent();
56004     },
56005
56006     renderRows : function(startRow, endRow){
56007         // pull in all the crap needed to render rows
56008         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56009         var colCount = cm.getColumnCount();
56010
56011         if(ds.getCount() < 1){
56012             return ["", ""];
56013         }
56014
56015         // build a map for all the columns
56016         var cs = [];
56017         for(var i = 0; i < colCount; i++){
56018             var name = cm.getDataIndex(i);
56019             cs[i] = {
56020                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56021                 renderer : cm.getRenderer(i),
56022                 id : cm.getColumnId(i),
56023                 locked : cm.isLocked(i),
56024                 has_editor : cm.isCellEditable(i)
56025             };
56026         }
56027
56028         startRow = startRow || 0;
56029         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56030
56031         // records to render
56032         var rs = ds.getRange(startRow, endRow);
56033
56034         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56035     },
56036
56037     // As much as I hate to duplicate code, this was branched because FireFox really hates
56038     // [].join("") on strings. The performance difference was substantial enough to
56039     // branch this function
56040     doRender : Roo.isGecko ?
56041             function(cs, rs, ds, startRow, colCount, stripe){
56042                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56043                 // buffers
56044                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56045                 
56046                 var hasListener = this.grid.hasListener('rowclass');
56047                 var rowcfg = {};
56048                 for(var j = 0, len = rs.length; j < len; j++){
56049                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56050                     for(var i = 0; i < colCount; i++){
56051                         c = cs[i];
56052                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56053                         p.id = c.id;
56054                         p.css = p.attr = "";
56055                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56056                         if(p.value == undefined || p.value === "") {
56057                             p.value = "&#160;";
56058                         }
56059                         if(c.has_editor){
56060                             p.css += ' x-grid-editable-cell';
56061                         }
56062                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56063                             p.css +=  ' x-grid-dirty-cell';
56064                         }
56065                         var markup = ct.apply(p);
56066                         if(!c.locked){
56067                             cb+= markup;
56068                         }else{
56069                             lcb+= markup;
56070                         }
56071                     }
56072                     var alt = [];
56073                     if(stripe && ((rowIndex+1) % 2 == 0)){
56074                         alt.push("x-grid-row-alt")
56075                     }
56076                     if(r.dirty){
56077                         alt.push(  " x-grid-dirty-row");
56078                     }
56079                     rp.cells = lcb;
56080                     if(this.getRowClass){
56081                         alt.push(this.getRowClass(r, rowIndex));
56082                     }
56083                     if (hasListener) {
56084                         rowcfg = {
56085                              
56086                             record: r,
56087                             rowIndex : rowIndex,
56088                             rowClass : ''
56089                         };
56090                         this.grid.fireEvent('rowclass', this, rowcfg);
56091                         alt.push(rowcfg.rowClass);
56092                     }
56093                     rp.alt = alt.join(" ");
56094                     lbuf+= rt.apply(rp);
56095                     rp.cells = cb;
56096                     buf+=  rt.apply(rp);
56097                 }
56098                 return [lbuf, buf];
56099             } :
56100             function(cs, rs, ds, startRow, colCount, stripe){
56101                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56102                 // buffers
56103                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56104                 var hasListener = this.grid.hasListener('rowclass');
56105  
56106                 var rowcfg = {};
56107                 for(var j = 0, len = rs.length; j < len; j++){
56108                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56109                     for(var i = 0; i < colCount; i++){
56110                         c = cs[i];
56111                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56112                         p.id = c.id;
56113                         p.css = p.attr = "";
56114                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56115                         if(p.value == undefined || p.value === "") {
56116                             p.value = "&#160;";
56117                         }
56118                         //Roo.log(c);
56119                          if(c.has_editor){
56120                             p.css += ' x-grid-editable-cell';
56121                         }
56122                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56123                             p.css += ' x-grid-dirty-cell' 
56124                         }
56125                         
56126                         var markup = ct.apply(p);
56127                         if(!c.locked){
56128                             cb[cb.length] = markup;
56129                         }else{
56130                             lcb[lcb.length] = markup;
56131                         }
56132                     }
56133                     var alt = [];
56134                     if(stripe && ((rowIndex+1) % 2 == 0)){
56135                         alt.push( "x-grid-row-alt");
56136                     }
56137                     if(r.dirty){
56138                         alt.push(" x-grid-dirty-row");
56139                     }
56140                     rp.cells = lcb;
56141                     if(this.getRowClass){
56142                         alt.push( this.getRowClass(r, rowIndex));
56143                     }
56144                     if (hasListener) {
56145                         rowcfg = {
56146                              
56147                             record: r,
56148                             rowIndex : rowIndex,
56149                             rowClass : ''
56150                         };
56151                         this.grid.fireEvent('rowclass', this, rowcfg);
56152                         alt.push(rowcfg.rowClass);
56153                     }
56154                     
56155                     rp.alt = alt.join(" ");
56156                     rp.cells = lcb.join("");
56157                     lbuf[lbuf.length] = rt.apply(rp);
56158                     rp.cells = cb.join("");
56159                     buf[buf.length] =  rt.apply(rp);
56160                 }
56161                 return [lbuf.join(""), buf.join("")];
56162             },
56163
56164     renderBody : function(){
56165         var markup = this.renderRows();
56166         var bt = this.templates.body;
56167         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56168     },
56169
56170     /**
56171      * Refreshes the grid
56172      * @param {Boolean} headersToo
56173      */
56174     refresh : function(headersToo){
56175         this.fireEvent("beforerefresh", this);
56176         this.grid.stopEditing();
56177         var result = this.renderBody();
56178         this.lockedBody.update(result[0]);
56179         this.mainBody.update(result[1]);
56180         if(headersToo === true){
56181             this.updateHeaders();
56182             this.updateColumns();
56183             this.updateSplitters();
56184             this.updateHeaderSortState();
56185         }
56186         this.syncRowHeights();
56187         this.layout();
56188         this.fireEvent("refresh", this);
56189     },
56190
56191     handleColumnMove : function(cm, oldIndex, newIndex){
56192         this.indexMap = null;
56193         var s = this.getScrollState();
56194         this.refresh(true);
56195         this.restoreScroll(s);
56196         this.afterMove(newIndex);
56197     },
56198
56199     afterMove : function(colIndex){
56200         if(this.enableMoveAnim && Roo.enableFx){
56201             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56202         }
56203         // if multisort - fix sortOrder, and reload..
56204         if (this.grid.dataSource.multiSort) {
56205             // the we can call sort again..
56206             var dm = this.grid.dataSource;
56207             var cm = this.grid.colModel;
56208             var so = [];
56209             for(var i = 0; i < cm.config.length; i++ ) {
56210                 
56211                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56212                     continue; // dont' bother, it's not in sort list or being set.
56213                 }
56214                 
56215                 so.push(cm.config[i].dataIndex);
56216             };
56217             dm.sortOrder = so;
56218             dm.load(dm.lastOptions);
56219             
56220             
56221         }
56222         
56223     },
56224
56225     updateCell : function(dm, rowIndex, dataIndex){
56226         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56227         if(typeof colIndex == "undefined"){ // not present in grid
56228             return;
56229         }
56230         var cm = this.grid.colModel;
56231         var cell = this.getCell(rowIndex, colIndex);
56232         var cellText = this.getCellText(rowIndex, colIndex);
56233
56234         var p = {
56235             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56236             id : cm.getColumnId(colIndex),
56237             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56238         };
56239         var renderer = cm.getRenderer(colIndex);
56240         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56241         if(typeof val == "undefined" || val === "") {
56242             val = "&#160;";
56243         }
56244         cellText.innerHTML = val;
56245         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56246         this.syncRowHeights(rowIndex, rowIndex);
56247     },
56248
56249     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56250         var maxWidth = 0;
56251         if(this.grid.autoSizeHeaders){
56252             var h = this.getHeaderCellMeasure(colIndex);
56253             maxWidth = Math.max(maxWidth, h.scrollWidth);
56254         }
56255         var tb, index;
56256         if(this.cm.isLocked(colIndex)){
56257             tb = this.getLockedTable();
56258             index = colIndex;
56259         }else{
56260             tb = this.getBodyTable();
56261             index = colIndex - this.cm.getLockedCount();
56262         }
56263         if(tb && tb.rows){
56264             var rows = tb.rows;
56265             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56266             for(var i = 0; i < stopIndex; i++){
56267                 var cell = rows[i].childNodes[index].firstChild;
56268                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56269             }
56270         }
56271         return maxWidth + /*margin for error in IE*/ 5;
56272     },
56273     /**
56274      * Autofit a column to its content.
56275      * @param {Number} colIndex
56276      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56277      */
56278      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56279          if(this.cm.isHidden(colIndex)){
56280              return; // can't calc a hidden column
56281          }
56282         if(forceMinSize){
56283             var cid = this.cm.getColumnId(colIndex);
56284             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56285            if(this.grid.autoSizeHeaders){
56286                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56287            }
56288         }
56289         var newWidth = this.calcColumnWidth(colIndex);
56290         this.cm.setColumnWidth(colIndex,
56291             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56292         if(!suppressEvent){
56293             this.grid.fireEvent("columnresize", colIndex, newWidth);
56294         }
56295     },
56296
56297     /**
56298      * Autofits all columns to their content and then expands to fit any extra space in the grid
56299      */
56300      autoSizeColumns : function(){
56301         var cm = this.grid.colModel;
56302         var colCount = cm.getColumnCount();
56303         for(var i = 0; i < colCount; i++){
56304             this.autoSizeColumn(i, true, true);
56305         }
56306         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56307             this.fitColumns();
56308         }else{
56309             this.updateColumns();
56310             this.layout();
56311         }
56312     },
56313
56314     /**
56315      * Autofits all columns to the grid's width proportionate with their current size
56316      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56317      */
56318     fitColumns : function(reserveScrollSpace){
56319         var cm = this.grid.colModel;
56320         var colCount = cm.getColumnCount();
56321         var cols = [];
56322         var width = 0;
56323         var i, w;
56324         for (i = 0; i < colCount; i++){
56325             if(!cm.isHidden(i) && !cm.isFixed(i)){
56326                 w = cm.getColumnWidth(i);
56327                 cols.push(i);
56328                 cols.push(w);
56329                 width += w;
56330             }
56331         }
56332         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56333         if(reserveScrollSpace){
56334             avail -= 17;
56335         }
56336         var frac = (avail - cm.getTotalWidth())/width;
56337         while (cols.length){
56338             w = cols.pop();
56339             i = cols.pop();
56340             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56341         }
56342         this.updateColumns();
56343         this.layout();
56344     },
56345
56346     onRowSelect : function(rowIndex){
56347         var row = this.getRowComposite(rowIndex);
56348         row.addClass("x-grid-row-selected");
56349     },
56350
56351     onRowDeselect : function(rowIndex){
56352         var row = this.getRowComposite(rowIndex);
56353         row.removeClass("x-grid-row-selected");
56354     },
56355
56356     onCellSelect : function(row, col){
56357         var cell = this.getCell(row, col);
56358         if(cell){
56359             Roo.fly(cell).addClass("x-grid-cell-selected");
56360         }
56361     },
56362
56363     onCellDeselect : function(row, col){
56364         var cell = this.getCell(row, col);
56365         if(cell){
56366             Roo.fly(cell).removeClass("x-grid-cell-selected");
56367         }
56368     },
56369
56370     updateHeaderSortState : function(){
56371         
56372         // sort state can be single { field: xxx, direction : yyy}
56373         // or   { xxx=>ASC , yyy : DESC ..... }
56374         
56375         var mstate = {};
56376         if (!this.ds.multiSort) { 
56377             var state = this.ds.getSortState();
56378             if(!state){
56379                 return;
56380             }
56381             mstate[state.field] = state.direction;
56382             // FIXME... - this is not used here.. but might be elsewhere..
56383             this.sortState = state;
56384             
56385         } else {
56386             mstate = this.ds.sortToggle;
56387         }
56388         //remove existing sort classes..
56389         
56390         var sc = this.sortClasses;
56391         var hds = this.el.select(this.headerSelector).removeClass(sc);
56392         
56393         for(var f in mstate) {
56394         
56395             var sortColumn = this.cm.findColumnIndex(f);
56396             
56397             if(sortColumn != -1){
56398                 var sortDir = mstate[f];        
56399                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56400             }
56401         }
56402         
56403          
56404         
56405     },
56406
56407
56408     handleHeaderClick : function(g, index,e){
56409         
56410         Roo.log("header click");
56411         
56412         if (Roo.isTouch) {
56413             // touch events on header are handled by context
56414             this.handleHdCtx(g,index,e);
56415             return;
56416         }
56417         
56418         
56419         if(this.headersDisabled){
56420             return;
56421         }
56422         var dm = g.dataSource, cm = g.colModel;
56423         if(!cm.isSortable(index)){
56424             return;
56425         }
56426         g.stopEditing();
56427         
56428         if (dm.multiSort) {
56429             // update the sortOrder
56430             var so = [];
56431             for(var i = 0; i < cm.config.length; i++ ) {
56432                 
56433                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56434                     continue; // dont' bother, it's not in sort list or being set.
56435                 }
56436                 
56437                 so.push(cm.config[i].dataIndex);
56438             };
56439             dm.sortOrder = so;
56440         }
56441         
56442         
56443         dm.sort(cm.getDataIndex(index));
56444     },
56445
56446
56447     destroy : function(){
56448         if(this.colMenu){
56449             this.colMenu.removeAll();
56450             Roo.menu.MenuMgr.unregister(this.colMenu);
56451             this.colMenu.getEl().remove();
56452             delete this.colMenu;
56453         }
56454         if(this.hmenu){
56455             this.hmenu.removeAll();
56456             Roo.menu.MenuMgr.unregister(this.hmenu);
56457             this.hmenu.getEl().remove();
56458             delete this.hmenu;
56459         }
56460         if(this.grid.enableColumnMove){
56461             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56462             if(dds){
56463                 for(var dd in dds){
56464                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56465                         var elid = dds[dd].dragElId;
56466                         dds[dd].unreg();
56467                         Roo.get(elid).remove();
56468                     } else if(dds[dd].config.isTarget){
56469                         dds[dd].proxyTop.remove();
56470                         dds[dd].proxyBottom.remove();
56471                         dds[dd].unreg();
56472                     }
56473                     if(Roo.dd.DDM.locationCache[dd]){
56474                         delete Roo.dd.DDM.locationCache[dd];
56475                     }
56476                 }
56477                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56478             }
56479         }
56480         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56481         this.bind(null, null);
56482         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56483     },
56484
56485     handleLockChange : function(){
56486         this.refresh(true);
56487     },
56488
56489     onDenyColumnLock : function(){
56490
56491     },
56492
56493     onDenyColumnHide : function(){
56494
56495     },
56496
56497     handleHdMenuClick : function(item){
56498         var index = this.hdCtxIndex;
56499         var cm = this.cm, ds = this.ds;
56500         switch(item.id){
56501             case "asc":
56502                 ds.sort(cm.getDataIndex(index), "ASC");
56503                 break;
56504             case "desc":
56505                 ds.sort(cm.getDataIndex(index), "DESC");
56506                 break;
56507             case "lock":
56508                 var lc = cm.getLockedCount();
56509                 if(cm.getColumnCount(true) <= lc+1){
56510                     this.onDenyColumnLock();
56511                     return;
56512                 }
56513                 if(lc != index){
56514                     cm.setLocked(index, true, true);
56515                     cm.moveColumn(index, lc);
56516                     this.grid.fireEvent("columnmove", index, lc);
56517                 }else{
56518                     cm.setLocked(index, true);
56519                 }
56520             break;
56521             case "unlock":
56522                 var lc = cm.getLockedCount();
56523                 if((lc-1) != index){
56524                     cm.setLocked(index, false, true);
56525                     cm.moveColumn(index, lc-1);
56526                     this.grid.fireEvent("columnmove", index, lc-1);
56527                 }else{
56528                     cm.setLocked(index, false);
56529                 }
56530             break;
56531             case 'wider': // used to expand cols on touch..
56532             case 'narrow':
56533                 var cw = cm.getColumnWidth(index);
56534                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56535                 cw = Math.max(0, cw);
56536                 cw = Math.min(cw,4000);
56537                 cm.setColumnWidth(index, cw);
56538                 break;
56539                 
56540             default:
56541                 index = cm.getIndexById(item.id.substr(4));
56542                 if(index != -1){
56543                     if(item.checked && cm.getColumnCount(true) <= 1){
56544                         this.onDenyColumnHide();
56545                         return false;
56546                     }
56547                     cm.setHidden(index, item.checked);
56548                 }
56549         }
56550         return true;
56551     },
56552
56553     beforeColMenuShow : function(){
56554         var cm = this.cm,  colCount = cm.getColumnCount();
56555         this.colMenu.removeAll();
56556         for(var i = 0; i < colCount; i++){
56557             this.colMenu.add(new Roo.menu.CheckItem({
56558                 id: "col-"+cm.getColumnId(i),
56559                 text: cm.getColumnHeader(i),
56560                 checked: !cm.isHidden(i),
56561                 hideOnClick:false
56562             }));
56563         }
56564     },
56565
56566     handleHdCtx : function(g, index, e){
56567         e.stopEvent();
56568         var hd = this.getHeaderCell(index);
56569         this.hdCtxIndex = index;
56570         var ms = this.hmenu.items, cm = this.cm;
56571         ms.get("asc").setDisabled(!cm.isSortable(index));
56572         ms.get("desc").setDisabled(!cm.isSortable(index));
56573         if(this.grid.enableColLock !== false){
56574             ms.get("lock").setDisabled(cm.isLocked(index));
56575             ms.get("unlock").setDisabled(!cm.isLocked(index));
56576         }
56577         this.hmenu.show(hd, "tl-bl");
56578     },
56579
56580     handleHdOver : function(e){
56581         var hd = this.findHeaderCell(e.getTarget());
56582         if(hd && !this.headersDisabled){
56583             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56584                this.fly(hd).addClass("x-grid-hd-over");
56585             }
56586         }
56587     },
56588
56589     handleHdOut : function(e){
56590         var hd = this.findHeaderCell(e.getTarget());
56591         if(hd){
56592             this.fly(hd).removeClass("x-grid-hd-over");
56593         }
56594     },
56595
56596     handleSplitDblClick : function(e, t){
56597         var i = this.getCellIndex(t);
56598         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56599             this.autoSizeColumn(i, true);
56600             this.layout();
56601         }
56602     },
56603
56604     render : function(){
56605
56606         var cm = this.cm;
56607         var colCount = cm.getColumnCount();
56608
56609         if(this.grid.monitorWindowResize === true){
56610             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56611         }
56612         var header = this.renderHeaders();
56613         var body = this.templates.body.apply({rows:""});
56614         var html = this.templates.master.apply({
56615             lockedBody: body,
56616             body: body,
56617             lockedHeader: header[0],
56618             header: header[1]
56619         });
56620
56621         //this.updateColumns();
56622
56623         this.grid.getGridEl().dom.innerHTML = html;
56624
56625         this.initElements();
56626         
56627         // a kludge to fix the random scolling effect in webkit
56628         this.el.on("scroll", function() {
56629             this.el.dom.scrollTop=0; // hopefully not recursive..
56630         },this);
56631
56632         this.scroller.on("scroll", this.handleScroll, this);
56633         this.lockedBody.on("mousewheel", this.handleWheel, this);
56634         this.mainBody.on("mousewheel", this.handleWheel, this);
56635
56636         this.mainHd.on("mouseover", this.handleHdOver, this);
56637         this.mainHd.on("mouseout", this.handleHdOut, this);
56638         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56639                 {delegate: "."+this.splitClass});
56640
56641         this.lockedHd.on("mouseover", this.handleHdOver, this);
56642         this.lockedHd.on("mouseout", this.handleHdOut, this);
56643         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56644                 {delegate: "."+this.splitClass});
56645
56646         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56647             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56648         }
56649
56650         this.updateSplitters();
56651
56652         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56653             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56654             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56655         }
56656
56657         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56658             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56659             this.hmenu.add(
56660                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56661                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56662             );
56663             if(this.grid.enableColLock !== false){
56664                 this.hmenu.add('-',
56665                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56666                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56667                 );
56668             }
56669             if (Roo.isTouch) {
56670                  this.hmenu.add('-',
56671                     {id:"wider", text: this.columnsWiderText},
56672                     {id:"narrow", text: this.columnsNarrowText }
56673                 );
56674                 
56675                  
56676             }
56677             
56678             if(this.grid.enableColumnHide !== false){
56679
56680                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56681                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56682                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56683
56684                 this.hmenu.add('-',
56685                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56686                 );
56687             }
56688             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56689
56690             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56691         }
56692
56693         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56694             this.dd = new Roo.grid.GridDragZone(this.grid, {
56695                 ddGroup : this.grid.ddGroup || 'GridDD'
56696             });
56697             
56698         }
56699
56700         /*
56701         for(var i = 0; i < colCount; i++){
56702             if(cm.isHidden(i)){
56703                 this.hideColumn(i);
56704             }
56705             if(cm.config[i].align){
56706                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56707                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56708             }
56709         }*/
56710         
56711         this.updateHeaderSortState();
56712
56713         this.beforeInitialResize();
56714         this.layout(true);
56715
56716         // two part rendering gives faster view to the user
56717         this.renderPhase2.defer(1, this);
56718     },
56719
56720     renderPhase2 : function(){
56721         // render the rows now
56722         this.refresh();
56723         if(this.grid.autoSizeColumns){
56724             this.autoSizeColumns();
56725         }
56726     },
56727
56728     beforeInitialResize : function(){
56729
56730     },
56731
56732     onColumnSplitterMoved : function(i, w){
56733         this.userResized = true;
56734         var cm = this.grid.colModel;
56735         cm.setColumnWidth(i, w, true);
56736         var cid = cm.getColumnId(i);
56737         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56738         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56739         this.updateSplitters();
56740         this.layout();
56741         this.grid.fireEvent("columnresize", i, w);
56742     },
56743
56744     syncRowHeights : function(startIndex, endIndex){
56745         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56746             startIndex = startIndex || 0;
56747             var mrows = this.getBodyTable().rows;
56748             var lrows = this.getLockedTable().rows;
56749             var len = mrows.length-1;
56750             endIndex = Math.min(endIndex || len, len);
56751             for(var i = startIndex; i <= endIndex; i++){
56752                 var m = mrows[i], l = lrows[i];
56753                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56754                 m.style.height = l.style.height = h + "px";
56755             }
56756         }
56757     },
56758
56759     layout : function(initialRender, is2ndPass){
56760         var g = this.grid;
56761         var auto = g.autoHeight;
56762         var scrollOffset = 16;
56763         var c = g.getGridEl(), cm = this.cm,
56764                 expandCol = g.autoExpandColumn,
56765                 gv = this;
56766         //c.beginMeasure();
56767
56768         if(!c.dom.offsetWidth){ // display:none?
56769             if(initialRender){
56770                 this.lockedWrap.show();
56771                 this.mainWrap.show();
56772             }
56773             return;
56774         }
56775
56776         var hasLock = this.cm.isLocked(0);
56777
56778         var tbh = this.headerPanel.getHeight();
56779         var bbh = this.footerPanel.getHeight();
56780
56781         if(auto){
56782             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56783             var newHeight = ch + c.getBorderWidth("tb");
56784             if(g.maxHeight){
56785                 newHeight = Math.min(g.maxHeight, newHeight);
56786             }
56787             c.setHeight(newHeight);
56788         }
56789
56790         if(g.autoWidth){
56791             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56792         }
56793
56794         var s = this.scroller;
56795
56796         var csize = c.getSize(true);
56797
56798         this.el.setSize(csize.width, csize.height);
56799
56800         this.headerPanel.setWidth(csize.width);
56801         this.footerPanel.setWidth(csize.width);
56802
56803         var hdHeight = this.mainHd.getHeight();
56804         var vw = csize.width;
56805         var vh = csize.height - (tbh + bbh);
56806
56807         s.setSize(vw, vh);
56808
56809         var bt = this.getBodyTable();
56810         
56811         if(cm.getLockedCount() == cm.config.length){
56812             bt = this.getLockedTable();
56813         }
56814         
56815         var ltWidth = hasLock ?
56816                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56817
56818         var scrollHeight = bt.offsetHeight;
56819         var scrollWidth = ltWidth + bt.offsetWidth;
56820         var vscroll = false, hscroll = false;
56821
56822         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56823
56824         var lw = this.lockedWrap, mw = this.mainWrap;
56825         var lb = this.lockedBody, mb = this.mainBody;
56826
56827         setTimeout(function(){
56828             var t = s.dom.offsetTop;
56829             var w = s.dom.clientWidth,
56830                 h = s.dom.clientHeight;
56831
56832             lw.setTop(t);
56833             lw.setSize(ltWidth, h);
56834
56835             mw.setLeftTop(ltWidth, t);
56836             mw.setSize(w-ltWidth, h);
56837
56838             lb.setHeight(h-hdHeight);
56839             mb.setHeight(h-hdHeight);
56840
56841             if(is2ndPass !== true && !gv.userResized && expandCol){
56842                 // high speed resize without full column calculation
56843                 
56844                 var ci = cm.getIndexById(expandCol);
56845                 if (ci < 0) {
56846                     ci = cm.findColumnIndex(expandCol);
56847                 }
56848                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56849                 var expandId = cm.getColumnId(ci);
56850                 var  tw = cm.getTotalWidth(false);
56851                 var currentWidth = cm.getColumnWidth(ci);
56852                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56853                 if(currentWidth != cw){
56854                     cm.setColumnWidth(ci, cw, true);
56855                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56856                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56857                     gv.updateSplitters();
56858                     gv.layout(false, true);
56859                 }
56860             }
56861
56862             if(initialRender){
56863                 lw.show();
56864                 mw.show();
56865             }
56866             //c.endMeasure();
56867         }, 10);
56868     },
56869
56870     onWindowResize : function(){
56871         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56872             return;
56873         }
56874         this.layout();
56875     },
56876
56877     appendFooter : function(parentEl){
56878         return null;
56879     },
56880
56881     sortAscText : "Sort Ascending",
56882     sortDescText : "Sort Descending",
56883     lockText : "Lock Column",
56884     unlockText : "Unlock Column",
56885     columnsText : "Columns",
56886  
56887     columnsWiderText : "Wider",
56888     columnsNarrowText : "Thinner"
56889 });
56890
56891
56892 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56893     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56894     this.proxy.el.addClass('x-grid3-col-dd');
56895 };
56896
56897 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56898     handleMouseDown : function(e){
56899
56900     },
56901
56902     callHandleMouseDown : function(e){
56903         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56904     }
56905 });
56906 /*
56907  * Based on:
56908  * Ext JS Library 1.1.1
56909  * Copyright(c) 2006-2007, Ext JS, LLC.
56910  *
56911  * Originally Released Under LGPL - original licence link has changed is not relivant.
56912  *
56913  * Fork - LGPL
56914  * <script type="text/javascript">
56915  */
56916  
56917 // private
56918 // This is a support class used internally by the Grid components
56919 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56920     this.grid = grid;
56921     this.view = grid.getView();
56922     this.proxy = this.view.resizeProxy;
56923     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56924         "gridSplitters" + this.grid.getGridEl().id, {
56925         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56926     });
56927     this.setHandleElId(Roo.id(hd));
56928     this.setOuterHandleElId(Roo.id(hd2));
56929     this.scroll = false;
56930 };
56931 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56932     fly: Roo.Element.fly,
56933
56934     b4StartDrag : function(x, y){
56935         this.view.headersDisabled = true;
56936         this.proxy.setHeight(this.view.mainWrap.getHeight());
56937         var w = this.cm.getColumnWidth(this.cellIndex);
56938         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56939         this.resetConstraints();
56940         this.setXConstraint(minw, 1000);
56941         this.setYConstraint(0, 0);
56942         this.minX = x - minw;
56943         this.maxX = x + 1000;
56944         this.startPos = x;
56945         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56946     },
56947
56948
56949     handleMouseDown : function(e){
56950         ev = Roo.EventObject.setEvent(e);
56951         var t = this.fly(ev.getTarget());
56952         if(t.hasClass("x-grid-split")){
56953             this.cellIndex = this.view.getCellIndex(t.dom);
56954             this.split = t.dom;
56955             this.cm = this.grid.colModel;
56956             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56957                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56958             }
56959         }
56960     },
56961
56962     endDrag : function(e){
56963         this.view.headersDisabled = false;
56964         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56965         var diff = endX - this.startPos;
56966         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56967     },
56968
56969     autoOffset : function(){
56970         this.setDelta(0,0);
56971     }
56972 });/*
56973  * Based on:
56974  * Ext JS Library 1.1.1
56975  * Copyright(c) 2006-2007, Ext JS, LLC.
56976  *
56977  * Originally Released Under LGPL - original licence link has changed is not relivant.
56978  *
56979  * Fork - LGPL
56980  * <script type="text/javascript">
56981  */
56982  
56983 // private
56984 // This is a support class used internally by the Grid components
56985 Roo.grid.GridDragZone = function(grid, config){
56986     this.view = grid.getView();
56987     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56988     if(this.view.lockedBody){
56989         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56990         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56991     }
56992     this.scroll = false;
56993     this.grid = grid;
56994     this.ddel = document.createElement('div');
56995     this.ddel.className = 'x-grid-dd-wrap';
56996 };
56997
56998 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56999     ddGroup : "GridDD",
57000
57001     getDragData : function(e){
57002         var t = Roo.lib.Event.getTarget(e);
57003         var rowIndex = this.view.findRowIndex(t);
57004         var sm = this.grid.selModel;
57005             
57006         //Roo.log(rowIndex);
57007         
57008         if (sm.getSelectedCell) {
57009             // cell selection..
57010             if (!sm.getSelectedCell()) {
57011                 return false;
57012             }
57013             if (rowIndex != sm.getSelectedCell()[0]) {
57014                 return false;
57015             }
57016         
57017         }
57018         
57019         if(rowIndex !== false){
57020             
57021             // if editorgrid.. 
57022             
57023             
57024             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57025                
57026             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57027               //  
57028             //}
57029             if (e.hasModifier()){
57030                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57031             }
57032             
57033             Roo.log("getDragData");
57034             
57035             return {
57036                 grid: this.grid,
57037                 ddel: this.ddel,
57038                 rowIndex: rowIndex,
57039                 selections:sm.getSelections ? sm.getSelections() : (
57040                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57041                 )
57042             };
57043         }
57044         return false;
57045     },
57046
57047     onInitDrag : function(e){
57048         var data = this.dragData;
57049         this.ddel.innerHTML = this.grid.getDragDropText();
57050         this.proxy.update(this.ddel);
57051         // fire start drag?
57052     },
57053
57054     afterRepair : function(){
57055         this.dragging = false;
57056     },
57057
57058     getRepairXY : function(e, data){
57059         return false;
57060     },
57061
57062     onEndDrag : function(data, e){
57063         // fire end drag?
57064     },
57065
57066     onValidDrop : function(dd, e, id){
57067         // fire drag drop?
57068         this.hideProxy();
57069     },
57070
57071     beforeInvalidDrop : function(e, id){
57072
57073     }
57074 });/*
57075  * Based on:
57076  * Ext JS Library 1.1.1
57077  * Copyright(c) 2006-2007, Ext JS, LLC.
57078  *
57079  * Originally Released Under LGPL - original licence link has changed is not relivant.
57080  *
57081  * Fork - LGPL
57082  * <script type="text/javascript">
57083  */
57084  
57085
57086 /**
57087  * @class Roo.grid.ColumnModel
57088  * @extends Roo.util.Observable
57089  * This is the default implementation of a ColumnModel used by the Grid. It defines
57090  * the columns in the grid.
57091  * <br>Usage:<br>
57092  <pre><code>
57093  var colModel = new Roo.grid.ColumnModel([
57094         {header: "Ticker", width: 60, sortable: true, locked: true},
57095         {header: "Company Name", width: 150, sortable: true},
57096         {header: "Market Cap.", width: 100, sortable: true},
57097         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57098         {header: "Employees", width: 100, sortable: true, resizable: false}
57099  ]);
57100  </code></pre>
57101  * <p>
57102  
57103  * The config options listed for this class are options which may appear in each
57104  * individual column definition.
57105  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57106  * @constructor
57107  * @param {Object} config An Array of column config objects. See this class's
57108  * config objects for details.
57109 */
57110 Roo.grid.ColumnModel = function(config){
57111         /**
57112      * The config passed into the constructor
57113      */
57114     this.config = config;
57115     this.lookup = {};
57116
57117     // if no id, create one
57118     // if the column does not have a dataIndex mapping,
57119     // map it to the order it is in the config
57120     for(var i = 0, len = config.length; i < len; i++){
57121         var c = config[i];
57122         if(typeof c.dataIndex == "undefined"){
57123             c.dataIndex = i;
57124         }
57125         if(typeof c.renderer == "string"){
57126             c.renderer = Roo.util.Format[c.renderer];
57127         }
57128         if(typeof c.id == "undefined"){
57129             c.id = Roo.id();
57130         }
57131         if(c.editor && c.editor.xtype){
57132             c.editor  = Roo.factory(c.editor, Roo.grid);
57133         }
57134         if(c.editor && c.editor.isFormField){
57135             c.editor = new Roo.grid.GridEditor(c.editor);
57136         }
57137         this.lookup[c.id] = c;
57138     }
57139
57140     /**
57141      * The width of columns which have no width specified (defaults to 100)
57142      * @type Number
57143      */
57144     this.defaultWidth = 100;
57145
57146     /**
57147      * Default sortable of columns which have no sortable specified (defaults to false)
57148      * @type Boolean
57149      */
57150     this.defaultSortable = false;
57151
57152     this.addEvents({
57153         /**
57154              * @event widthchange
57155              * Fires when the width of a column changes.
57156              * @param {ColumnModel} this
57157              * @param {Number} columnIndex The column index
57158              * @param {Number} newWidth The new width
57159              */
57160             "widthchange": true,
57161         /**
57162              * @event headerchange
57163              * Fires when the text of a header changes.
57164              * @param {ColumnModel} this
57165              * @param {Number} columnIndex The column index
57166              * @param {Number} newText The new header text
57167              */
57168             "headerchange": true,
57169         /**
57170              * @event hiddenchange
57171              * Fires when a column is hidden or "unhidden".
57172              * @param {ColumnModel} this
57173              * @param {Number} columnIndex The column index
57174              * @param {Boolean} hidden true if hidden, false otherwise
57175              */
57176             "hiddenchange": true,
57177             /**
57178          * @event columnmoved
57179          * Fires when a column is moved.
57180          * @param {ColumnModel} this
57181          * @param {Number} oldIndex
57182          * @param {Number} newIndex
57183          */
57184         "columnmoved" : true,
57185         /**
57186          * @event columlockchange
57187          * Fires when a column's locked state is changed
57188          * @param {ColumnModel} this
57189          * @param {Number} colIndex
57190          * @param {Boolean} locked true if locked
57191          */
57192         "columnlockchange" : true
57193     });
57194     Roo.grid.ColumnModel.superclass.constructor.call(this);
57195 };
57196 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57197     /**
57198      * @cfg {String} header The header text to display in the Grid view.
57199      */
57200     /**
57201      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57202      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57203      * specified, the column's index is used as an index into the Record's data Array.
57204      */
57205     /**
57206      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57207      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57208      */
57209     /**
57210      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57211      * Defaults to the value of the {@link #defaultSortable} property.
57212      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57213      */
57214     /**
57215      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57216      */
57217     /**
57218      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57219      */
57220     /**
57221      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57222      */
57223     /**
57224      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57225      */
57226     /**
57227      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57228      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57229      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57230      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57231      */
57232        /**
57233      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57234      */
57235     /**
57236      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57237      */
57238     /**
57239      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57240      */
57241     /**
57242      * @cfg {String} cursor (Optional)
57243      */
57244     /**
57245      * @cfg {String} tooltip (Optional)
57246      */
57247     /**
57248      * @cfg {Number} xs (Optional)
57249      */
57250     /**
57251      * @cfg {Number} sm (Optional)
57252      */
57253     /**
57254      * @cfg {Number} md (Optional)
57255      */
57256     /**
57257      * @cfg {Number} lg (Optional)
57258      */
57259     /**
57260      * Returns the id of the column at the specified index.
57261      * @param {Number} index The column index
57262      * @return {String} the id
57263      */
57264     getColumnId : function(index){
57265         return this.config[index].id;
57266     },
57267
57268     /**
57269      * Returns the column for a specified id.
57270      * @param {String} id The column id
57271      * @return {Object} the column
57272      */
57273     getColumnById : function(id){
57274         return this.lookup[id];
57275     },
57276
57277     
57278     /**
57279      * Returns the column for a specified dataIndex.
57280      * @param {String} dataIndex The column dataIndex
57281      * @return {Object|Boolean} the column or false if not found
57282      */
57283     getColumnByDataIndex: function(dataIndex){
57284         var index = this.findColumnIndex(dataIndex);
57285         return index > -1 ? this.config[index] : false;
57286     },
57287     
57288     /**
57289      * Returns the index for a specified column id.
57290      * @param {String} id The column id
57291      * @return {Number} the index, or -1 if not found
57292      */
57293     getIndexById : function(id){
57294         for(var i = 0, len = this.config.length; i < len; i++){
57295             if(this.config[i].id == id){
57296                 return i;
57297             }
57298         }
57299         return -1;
57300     },
57301     
57302     /**
57303      * Returns the index for a specified column dataIndex.
57304      * @param {String} dataIndex The column dataIndex
57305      * @return {Number} the index, or -1 if not found
57306      */
57307     
57308     findColumnIndex : function(dataIndex){
57309         for(var i = 0, len = this.config.length; i < len; i++){
57310             if(this.config[i].dataIndex == dataIndex){
57311                 return i;
57312             }
57313         }
57314         return -1;
57315     },
57316     
57317     
57318     moveColumn : function(oldIndex, newIndex){
57319         var c = this.config[oldIndex];
57320         this.config.splice(oldIndex, 1);
57321         this.config.splice(newIndex, 0, c);
57322         this.dataMap = null;
57323         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57324     },
57325
57326     isLocked : function(colIndex){
57327         return this.config[colIndex].locked === true;
57328     },
57329
57330     setLocked : function(colIndex, value, suppressEvent){
57331         if(this.isLocked(colIndex) == value){
57332             return;
57333         }
57334         this.config[colIndex].locked = value;
57335         if(!suppressEvent){
57336             this.fireEvent("columnlockchange", this, colIndex, value);
57337         }
57338     },
57339
57340     getTotalLockedWidth : function(){
57341         var totalWidth = 0;
57342         for(var i = 0; i < this.config.length; i++){
57343             if(this.isLocked(i) && !this.isHidden(i)){
57344                 this.totalWidth += this.getColumnWidth(i);
57345             }
57346         }
57347         return totalWidth;
57348     },
57349
57350     getLockedCount : function(){
57351         for(var i = 0, len = this.config.length; i < len; i++){
57352             if(!this.isLocked(i)){
57353                 return i;
57354             }
57355         }
57356         
57357         return this.config.length;
57358     },
57359
57360     /**
57361      * Returns the number of columns.
57362      * @return {Number}
57363      */
57364     getColumnCount : function(visibleOnly){
57365         if(visibleOnly === true){
57366             var c = 0;
57367             for(var i = 0, len = this.config.length; i < len; i++){
57368                 if(!this.isHidden(i)){
57369                     c++;
57370                 }
57371             }
57372             return c;
57373         }
57374         return this.config.length;
57375     },
57376
57377     /**
57378      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57379      * @param {Function} fn
57380      * @param {Object} scope (optional)
57381      * @return {Array} result
57382      */
57383     getColumnsBy : function(fn, scope){
57384         var r = [];
57385         for(var i = 0, len = this.config.length; i < len; i++){
57386             var c = this.config[i];
57387             if(fn.call(scope||this, c, i) === true){
57388                 r[r.length] = c;
57389             }
57390         }
57391         return r;
57392     },
57393
57394     /**
57395      * Returns true if the specified column is sortable.
57396      * @param {Number} col The column index
57397      * @return {Boolean}
57398      */
57399     isSortable : function(col){
57400         if(typeof this.config[col].sortable == "undefined"){
57401             return this.defaultSortable;
57402         }
57403         return this.config[col].sortable;
57404     },
57405
57406     /**
57407      * Returns the rendering (formatting) function defined for the column.
57408      * @param {Number} col The column index.
57409      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57410      */
57411     getRenderer : function(col){
57412         if(!this.config[col].renderer){
57413             return Roo.grid.ColumnModel.defaultRenderer;
57414         }
57415         return this.config[col].renderer;
57416     },
57417
57418     /**
57419      * Sets the rendering (formatting) function for a column.
57420      * @param {Number} col The column index
57421      * @param {Function} fn The function to use to process the cell's raw data
57422      * to return HTML markup for the grid view. The render function is called with
57423      * the following parameters:<ul>
57424      * <li>Data value.</li>
57425      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57426      * <li>css A CSS style string to apply to the table cell.</li>
57427      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57428      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57429      * <li>Row index</li>
57430      * <li>Column index</li>
57431      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57432      */
57433     setRenderer : function(col, fn){
57434         this.config[col].renderer = fn;
57435     },
57436
57437     /**
57438      * Returns the width for the specified column.
57439      * @param {Number} col The column index
57440      * @return {Number}
57441      */
57442     getColumnWidth : function(col){
57443         return this.config[col].width * 1 || this.defaultWidth;
57444     },
57445
57446     /**
57447      * Sets the width for a column.
57448      * @param {Number} col The column index
57449      * @param {Number} width The new width
57450      */
57451     setColumnWidth : function(col, width, suppressEvent){
57452         this.config[col].width = width;
57453         this.totalWidth = null;
57454         if(!suppressEvent){
57455              this.fireEvent("widthchange", this, col, width);
57456         }
57457     },
57458
57459     /**
57460      * Returns the total width of all columns.
57461      * @param {Boolean} includeHidden True to include hidden column widths
57462      * @return {Number}
57463      */
57464     getTotalWidth : function(includeHidden){
57465         if(!this.totalWidth){
57466             this.totalWidth = 0;
57467             for(var i = 0, len = this.config.length; i < len; i++){
57468                 if(includeHidden || !this.isHidden(i)){
57469                     this.totalWidth += this.getColumnWidth(i);
57470                 }
57471             }
57472         }
57473         return this.totalWidth;
57474     },
57475
57476     /**
57477      * Returns the header for the specified column.
57478      * @param {Number} col The column index
57479      * @return {String}
57480      */
57481     getColumnHeader : function(col){
57482         return this.config[col].header;
57483     },
57484
57485     /**
57486      * Sets the header for a column.
57487      * @param {Number} col The column index
57488      * @param {String} header The new header
57489      */
57490     setColumnHeader : function(col, header){
57491         this.config[col].header = header;
57492         this.fireEvent("headerchange", this, col, header);
57493     },
57494
57495     /**
57496      * Returns the tooltip for the specified column.
57497      * @param {Number} col The column index
57498      * @return {String}
57499      */
57500     getColumnTooltip : function(col){
57501             return this.config[col].tooltip;
57502     },
57503     /**
57504      * Sets the tooltip for a column.
57505      * @param {Number} col The column index
57506      * @param {String} tooltip The new tooltip
57507      */
57508     setColumnTooltip : function(col, tooltip){
57509             this.config[col].tooltip = tooltip;
57510     },
57511
57512     /**
57513      * Returns the dataIndex for the specified column.
57514      * @param {Number} col The column index
57515      * @return {Number}
57516      */
57517     getDataIndex : function(col){
57518         return this.config[col].dataIndex;
57519     },
57520
57521     /**
57522      * Sets the dataIndex for a column.
57523      * @param {Number} col The column index
57524      * @param {Number} dataIndex The new dataIndex
57525      */
57526     setDataIndex : function(col, dataIndex){
57527         this.config[col].dataIndex = dataIndex;
57528     },
57529
57530     
57531     
57532     /**
57533      * Returns true if the cell is editable.
57534      * @param {Number} colIndex The column index
57535      * @param {Number} rowIndex The row index - this is nto actually used..?
57536      * @return {Boolean}
57537      */
57538     isCellEditable : function(colIndex, rowIndex){
57539         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57540     },
57541
57542     /**
57543      * Returns the editor defined for the cell/column.
57544      * return false or null to disable editing.
57545      * @param {Number} colIndex The column index
57546      * @param {Number} rowIndex The row index
57547      * @return {Object}
57548      */
57549     getCellEditor : function(colIndex, rowIndex){
57550         return this.config[colIndex].editor;
57551     },
57552
57553     /**
57554      * Sets if a column is editable.
57555      * @param {Number} col The column index
57556      * @param {Boolean} editable True if the column is editable
57557      */
57558     setEditable : function(col, editable){
57559         this.config[col].editable = editable;
57560     },
57561
57562
57563     /**
57564      * Returns true if the column is hidden.
57565      * @param {Number} colIndex The column index
57566      * @return {Boolean}
57567      */
57568     isHidden : function(colIndex){
57569         return this.config[colIndex].hidden;
57570     },
57571
57572
57573     /**
57574      * Returns true if the column width cannot be changed
57575      */
57576     isFixed : function(colIndex){
57577         return this.config[colIndex].fixed;
57578     },
57579
57580     /**
57581      * Returns true if the column can be resized
57582      * @return {Boolean}
57583      */
57584     isResizable : function(colIndex){
57585         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57586     },
57587     /**
57588      * Sets if a column is hidden.
57589      * @param {Number} colIndex The column index
57590      * @param {Boolean} hidden True if the column is hidden
57591      */
57592     setHidden : function(colIndex, hidden){
57593         this.config[colIndex].hidden = hidden;
57594         this.totalWidth = null;
57595         this.fireEvent("hiddenchange", this, colIndex, hidden);
57596     },
57597
57598     /**
57599      * Sets the editor for a column.
57600      * @param {Number} col The column index
57601      * @param {Object} editor The editor object
57602      */
57603     setEditor : function(col, editor){
57604         this.config[col].editor = editor;
57605     }
57606 });
57607
57608 Roo.grid.ColumnModel.defaultRenderer = function(value)
57609 {
57610     if(typeof value == "object") {
57611         return value;
57612     }
57613         if(typeof value == "string" && value.length < 1){
57614             return "&#160;";
57615         }
57616     
57617         return String.format("{0}", value);
57618 };
57619
57620 // Alias for backwards compatibility
57621 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57622 /*
57623  * Based on:
57624  * Ext JS Library 1.1.1
57625  * Copyright(c) 2006-2007, Ext JS, LLC.
57626  *
57627  * Originally Released Under LGPL - original licence link has changed is not relivant.
57628  *
57629  * Fork - LGPL
57630  * <script type="text/javascript">
57631  */
57632
57633 /**
57634  * @class Roo.grid.AbstractSelectionModel
57635  * @extends Roo.util.Observable
57636  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57637  * implemented by descendant classes.  This class should not be directly instantiated.
57638  * @constructor
57639  */
57640 Roo.grid.AbstractSelectionModel = function(){
57641     this.locked = false;
57642     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57643 };
57644
57645 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57646     /** @ignore Called by the grid automatically. Do not call directly. */
57647     init : function(grid){
57648         this.grid = grid;
57649         this.initEvents();
57650     },
57651
57652     /**
57653      * Locks the selections.
57654      */
57655     lock : function(){
57656         this.locked = true;
57657     },
57658
57659     /**
57660      * Unlocks the selections.
57661      */
57662     unlock : function(){
57663         this.locked = false;
57664     },
57665
57666     /**
57667      * Returns true if the selections are locked.
57668      * @return {Boolean}
57669      */
57670     isLocked : function(){
57671         return this.locked;
57672     }
57673 });/*
57674  * Based on:
57675  * Ext JS Library 1.1.1
57676  * Copyright(c) 2006-2007, Ext JS, LLC.
57677  *
57678  * Originally Released Under LGPL - original licence link has changed is not relivant.
57679  *
57680  * Fork - LGPL
57681  * <script type="text/javascript">
57682  */
57683 /**
57684  * @extends Roo.grid.AbstractSelectionModel
57685  * @class Roo.grid.RowSelectionModel
57686  * The default SelectionModel used by {@link Roo.grid.Grid}.
57687  * It supports multiple selections and keyboard selection/navigation. 
57688  * @constructor
57689  * @param {Object} config
57690  */
57691 Roo.grid.RowSelectionModel = function(config){
57692     Roo.apply(this, config);
57693     this.selections = new Roo.util.MixedCollection(false, function(o){
57694         return o.id;
57695     });
57696
57697     this.last = false;
57698     this.lastActive = false;
57699
57700     this.addEvents({
57701         /**
57702              * @event selectionchange
57703              * Fires when the selection changes
57704              * @param {SelectionModel} this
57705              */
57706             "selectionchange" : true,
57707         /**
57708              * @event afterselectionchange
57709              * Fires after the selection changes (eg. by key press or clicking)
57710              * @param {SelectionModel} this
57711              */
57712             "afterselectionchange" : true,
57713         /**
57714              * @event beforerowselect
57715              * Fires when a row is selected being selected, return false to cancel.
57716              * @param {SelectionModel} this
57717              * @param {Number} rowIndex The selected index
57718              * @param {Boolean} keepExisting False if other selections will be cleared
57719              */
57720             "beforerowselect" : true,
57721         /**
57722              * @event rowselect
57723              * Fires when a row is selected.
57724              * @param {SelectionModel} this
57725              * @param {Number} rowIndex The selected index
57726              * @param {Roo.data.Record} r The record
57727              */
57728             "rowselect" : true,
57729         /**
57730              * @event rowdeselect
57731              * Fires when a row is deselected.
57732              * @param {SelectionModel} this
57733              * @param {Number} rowIndex The selected index
57734              */
57735         "rowdeselect" : true
57736     });
57737     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57738     this.locked = false;
57739 };
57740
57741 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57742     /**
57743      * @cfg {Boolean} singleSelect
57744      * True to allow selection of only one row at a time (defaults to false)
57745      */
57746     singleSelect : false,
57747
57748     // private
57749     initEvents : function(){
57750
57751         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57752             this.grid.on("mousedown", this.handleMouseDown, this);
57753         }else{ // allow click to work like normal
57754             this.grid.on("rowclick", this.handleDragableRowClick, this);
57755         }
57756
57757         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57758             "up" : function(e){
57759                 if(!e.shiftKey){
57760                     this.selectPrevious(e.shiftKey);
57761                 }else if(this.last !== false && this.lastActive !== false){
57762                     var last = this.last;
57763                     this.selectRange(this.last,  this.lastActive-1);
57764                     this.grid.getView().focusRow(this.lastActive);
57765                     if(last !== false){
57766                         this.last = last;
57767                     }
57768                 }else{
57769                     this.selectFirstRow();
57770                 }
57771                 this.fireEvent("afterselectionchange", this);
57772             },
57773             "down" : function(e){
57774                 if(!e.shiftKey){
57775                     this.selectNext(e.shiftKey);
57776                 }else if(this.last !== false && this.lastActive !== false){
57777                     var last = this.last;
57778                     this.selectRange(this.last,  this.lastActive+1);
57779                     this.grid.getView().focusRow(this.lastActive);
57780                     if(last !== false){
57781                         this.last = last;
57782                     }
57783                 }else{
57784                     this.selectFirstRow();
57785                 }
57786                 this.fireEvent("afterselectionchange", this);
57787             },
57788             scope: this
57789         });
57790
57791         var view = this.grid.view;
57792         view.on("refresh", this.onRefresh, this);
57793         view.on("rowupdated", this.onRowUpdated, this);
57794         view.on("rowremoved", this.onRemove, this);
57795     },
57796
57797     // private
57798     onRefresh : function(){
57799         var ds = this.grid.dataSource, i, v = this.grid.view;
57800         var s = this.selections;
57801         s.each(function(r){
57802             if((i = ds.indexOfId(r.id)) != -1){
57803                 v.onRowSelect(i);
57804                 s.add(ds.getAt(i)); // updating the selection relate data
57805             }else{
57806                 s.remove(r);
57807             }
57808         });
57809     },
57810
57811     // private
57812     onRemove : function(v, index, r){
57813         this.selections.remove(r);
57814     },
57815
57816     // private
57817     onRowUpdated : function(v, index, r){
57818         if(this.isSelected(r)){
57819             v.onRowSelect(index);
57820         }
57821     },
57822
57823     /**
57824      * Select records.
57825      * @param {Array} records The records to select
57826      * @param {Boolean} keepExisting (optional) True to keep existing selections
57827      */
57828     selectRecords : function(records, keepExisting){
57829         if(!keepExisting){
57830             this.clearSelections();
57831         }
57832         var ds = this.grid.dataSource;
57833         for(var i = 0, len = records.length; i < len; i++){
57834             this.selectRow(ds.indexOf(records[i]), true);
57835         }
57836     },
57837
57838     /**
57839      * Gets the number of selected rows.
57840      * @return {Number}
57841      */
57842     getCount : function(){
57843         return this.selections.length;
57844     },
57845
57846     /**
57847      * Selects the first row in the grid.
57848      */
57849     selectFirstRow : function(){
57850         this.selectRow(0);
57851     },
57852
57853     /**
57854      * Select the last row.
57855      * @param {Boolean} keepExisting (optional) True to keep existing selections
57856      */
57857     selectLastRow : function(keepExisting){
57858         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57859     },
57860
57861     /**
57862      * Selects the row immediately following the last selected row.
57863      * @param {Boolean} keepExisting (optional) True to keep existing selections
57864      */
57865     selectNext : function(keepExisting){
57866         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57867             this.selectRow(this.last+1, keepExisting);
57868             this.grid.getView().focusRow(this.last);
57869         }
57870     },
57871
57872     /**
57873      * Selects the row that precedes the last selected row.
57874      * @param {Boolean} keepExisting (optional) True to keep existing selections
57875      */
57876     selectPrevious : function(keepExisting){
57877         if(this.last){
57878             this.selectRow(this.last-1, keepExisting);
57879             this.grid.getView().focusRow(this.last);
57880         }
57881     },
57882
57883     /**
57884      * Returns the selected records
57885      * @return {Array} Array of selected records
57886      */
57887     getSelections : function(){
57888         return [].concat(this.selections.items);
57889     },
57890
57891     /**
57892      * Returns the first selected record.
57893      * @return {Record}
57894      */
57895     getSelected : function(){
57896         return this.selections.itemAt(0);
57897     },
57898
57899
57900     /**
57901      * Clears all selections.
57902      */
57903     clearSelections : function(fast){
57904         if(this.locked) {
57905             return;
57906         }
57907         if(fast !== true){
57908             var ds = this.grid.dataSource;
57909             var s = this.selections;
57910             s.each(function(r){
57911                 this.deselectRow(ds.indexOfId(r.id));
57912             }, this);
57913             s.clear();
57914         }else{
57915             this.selections.clear();
57916         }
57917         this.last = false;
57918     },
57919
57920
57921     /**
57922      * Selects all rows.
57923      */
57924     selectAll : function(){
57925         if(this.locked) {
57926             return;
57927         }
57928         this.selections.clear();
57929         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57930             this.selectRow(i, true);
57931         }
57932     },
57933
57934     /**
57935      * Returns True if there is a selection.
57936      * @return {Boolean}
57937      */
57938     hasSelection : function(){
57939         return this.selections.length > 0;
57940     },
57941
57942     /**
57943      * Returns True if the specified row is selected.
57944      * @param {Number/Record} record The record or index of the record to check
57945      * @return {Boolean}
57946      */
57947     isSelected : function(index){
57948         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57949         return (r && this.selections.key(r.id) ? true : false);
57950     },
57951
57952     /**
57953      * Returns True if the specified record id is selected.
57954      * @param {String} id The id of record to check
57955      * @return {Boolean}
57956      */
57957     isIdSelected : function(id){
57958         return (this.selections.key(id) ? true : false);
57959     },
57960
57961     // private
57962     handleMouseDown : function(e, t){
57963         var view = this.grid.getView(), rowIndex;
57964         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57965             return;
57966         };
57967         if(e.shiftKey && this.last !== false){
57968             var last = this.last;
57969             this.selectRange(last, rowIndex, e.ctrlKey);
57970             this.last = last; // reset the last
57971             view.focusRow(rowIndex);
57972         }else{
57973             var isSelected = this.isSelected(rowIndex);
57974             if(e.button !== 0 && isSelected){
57975                 view.focusRow(rowIndex);
57976             }else if(e.ctrlKey && isSelected){
57977                 this.deselectRow(rowIndex);
57978             }else if(!isSelected){
57979                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57980                 view.focusRow(rowIndex);
57981             }
57982         }
57983         this.fireEvent("afterselectionchange", this);
57984     },
57985     // private
57986     handleDragableRowClick :  function(grid, rowIndex, e) 
57987     {
57988         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57989             this.selectRow(rowIndex, false);
57990             grid.view.focusRow(rowIndex);
57991              this.fireEvent("afterselectionchange", this);
57992         }
57993     },
57994     
57995     /**
57996      * Selects multiple rows.
57997      * @param {Array} rows Array of the indexes of the row to select
57998      * @param {Boolean} keepExisting (optional) True to keep existing selections
57999      */
58000     selectRows : function(rows, keepExisting){
58001         if(!keepExisting){
58002             this.clearSelections();
58003         }
58004         for(var i = 0, len = rows.length; i < len; i++){
58005             this.selectRow(rows[i], true);
58006         }
58007     },
58008
58009     /**
58010      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58011      * @param {Number} startRow The index of the first row in the range
58012      * @param {Number} endRow The index of the last row in the range
58013      * @param {Boolean} keepExisting (optional) True to retain existing selections
58014      */
58015     selectRange : function(startRow, endRow, keepExisting){
58016         if(this.locked) {
58017             return;
58018         }
58019         if(!keepExisting){
58020             this.clearSelections();
58021         }
58022         if(startRow <= endRow){
58023             for(var i = startRow; i <= endRow; i++){
58024                 this.selectRow(i, true);
58025             }
58026         }else{
58027             for(var i = startRow; i >= endRow; i--){
58028                 this.selectRow(i, true);
58029             }
58030         }
58031     },
58032
58033     /**
58034      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58035      * @param {Number} startRow The index of the first row in the range
58036      * @param {Number} endRow The index of the last row in the range
58037      */
58038     deselectRange : function(startRow, endRow, preventViewNotify){
58039         if(this.locked) {
58040             return;
58041         }
58042         for(var i = startRow; i <= endRow; i++){
58043             this.deselectRow(i, preventViewNotify);
58044         }
58045     },
58046
58047     /**
58048      * Selects a row.
58049      * @param {Number} row The index of the row to select
58050      * @param {Boolean} keepExisting (optional) True to keep existing selections
58051      */
58052     selectRow : function(index, keepExisting, preventViewNotify){
58053         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58054             return;
58055         }
58056         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58057             if(!keepExisting || this.singleSelect){
58058                 this.clearSelections();
58059             }
58060             var r = this.grid.dataSource.getAt(index);
58061             this.selections.add(r);
58062             this.last = this.lastActive = index;
58063             if(!preventViewNotify){
58064                 this.grid.getView().onRowSelect(index);
58065             }
58066             this.fireEvent("rowselect", this, index, r);
58067             this.fireEvent("selectionchange", this);
58068         }
58069     },
58070
58071     /**
58072      * Deselects a row.
58073      * @param {Number} row The index of the row to deselect
58074      */
58075     deselectRow : function(index, preventViewNotify){
58076         if(this.locked) {
58077             return;
58078         }
58079         if(this.last == index){
58080             this.last = false;
58081         }
58082         if(this.lastActive == index){
58083             this.lastActive = false;
58084         }
58085         var r = this.grid.dataSource.getAt(index);
58086         this.selections.remove(r);
58087         if(!preventViewNotify){
58088             this.grid.getView().onRowDeselect(index);
58089         }
58090         this.fireEvent("rowdeselect", this, index);
58091         this.fireEvent("selectionchange", this);
58092     },
58093
58094     // private
58095     restoreLast : function(){
58096         if(this._last){
58097             this.last = this._last;
58098         }
58099     },
58100
58101     // private
58102     acceptsNav : function(row, col, cm){
58103         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58104     },
58105
58106     // private
58107     onEditorKey : function(field, e){
58108         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58109         if(k == e.TAB){
58110             e.stopEvent();
58111             ed.completeEdit();
58112             if(e.shiftKey){
58113                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58114             }else{
58115                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58116             }
58117         }else if(k == e.ENTER && !e.ctrlKey){
58118             e.stopEvent();
58119             ed.completeEdit();
58120             if(e.shiftKey){
58121                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58122             }else{
58123                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58124             }
58125         }else if(k == e.ESC){
58126             ed.cancelEdit();
58127         }
58128         if(newCell){
58129             g.startEditing(newCell[0], newCell[1]);
58130         }
58131     }
58132 });/*
58133  * Based on:
58134  * Ext JS Library 1.1.1
58135  * Copyright(c) 2006-2007, Ext JS, LLC.
58136  *
58137  * Originally Released Under LGPL - original licence link has changed is not relivant.
58138  *
58139  * Fork - LGPL
58140  * <script type="text/javascript">
58141  */
58142 /**
58143  * @class Roo.grid.CellSelectionModel
58144  * @extends Roo.grid.AbstractSelectionModel
58145  * This class provides the basic implementation for cell selection in a grid.
58146  * @constructor
58147  * @param {Object} config The object containing the configuration of this model.
58148  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58149  */
58150 Roo.grid.CellSelectionModel = function(config){
58151     Roo.apply(this, config);
58152
58153     this.selection = null;
58154
58155     this.addEvents({
58156         /**
58157              * @event beforerowselect
58158              * Fires before a cell is selected.
58159              * @param {SelectionModel} this
58160              * @param {Number} rowIndex The selected row index
58161              * @param {Number} colIndex The selected cell index
58162              */
58163             "beforecellselect" : true,
58164         /**
58165              * @event cellselect
58166              * Fires when a cell is selected.
58167              * @param {SelectionModel} this
58168              * @param {Number} rowIndex The selected row index
58169              * @param {Number} colIndex The selected cell index
58170              */
58171             "cellselect" : true,
58172         /**
58173              * @event selectionchange
58174              * Fires when the active selection changes.
58175              * @param {SelectionModel} this
58176              * @param {Object} selection null for no selection or an object (o) with two properties
58177                 <ul>
58178                 <li>o.record: the record object for the row the selection is in</li>
58179                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58180                 </ul>
58181              */
58182             "selectionchange" : true,
58183         /**
58184              * @event tabend
58185              * Fires when the tab (or enter) was pressed on the last editable cell
58186              * You can use this to trigger add new row.
58187              * @param {SelectionModel} this
58188              */
58189             "tabend" : true,
58190          /**
58191              * @event beforeeditnext
58192              * Fires before the next editable sell is made active
58193              * You can use this to skip to another cell or fire the tabend
58194              *    if you set cell to false
58195              * @param {Object} eventdata object : { cell : [ row, col ] } 
58196              */
58197             "beforeeditnext" : true
58198     });
58199     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58200 };
58201
58202 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58203     
58204     enter_is_tab: false,
58205
58206     /** @ignore */
58207     initEvents : function(){
58208         this.grid.on("mousedown", this.handleMouseDown, this);
58209         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58210         var view = this.grid.view;
58211         view.on("refresh", this.onViewChange, this);
58212         view.on("rowupdated", this.onRowUpdated, this);
58213         view.on("beforerowremoved", this.clearSelections, this);
58214         view.on("beforerowsinserted", this.clearSelections, this);
58215         if(this.grid.isEditor){
58216             this.grid.on("beforeedit", this.beforeEdit,  this);
58217         }
58218     },
58219
58220         //private
58221     beforeEdit : function(e){
58222         this.select(e.row, e.column, false, true, e.record);
58223     },
58224
58225         //private
58226     onRowUpdated : function(v, index, r){
58227         if(this.selection && this.selection.record == r){
58228             v.onCellSelect(index, this.selection.cell[1]);
58229         }
58230     },
58231
58232         //private
58233     onViewChange : function(){
58234         this.clearSelections(true);
58235     },
58236
58237         /**
58238          * Returns the currently selected cell,.
58239          * @return {Array} The selected cell (row, column) or null if none selected.
58240          */
58241     getSelectedCell : function(){
58242         return this.selection ? this.selection.cell : null;
58243     },
58244
58245     /**
58246      * Clears all selections.
58247      * @param {Boolean} true to prevent the gridview from being notified about the change.
58248      */
58249     clearSelections : function(preventNotify){
58250         var s = this.selection;
58251         if(s){
58252             if(preventNotify !== true){
58253                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58254             }
58255             this.selection = null;
58256             this.fireEvent("selectionchange", this, null);
58257         }
58258     },
58259
58260     /**
58261      * Returns true if there is a selection.
58262      * @return {Boolean}
58263      */
58264     hasSelection : function(){
58265         return this.selection ? true : false;
58266     },
58267
58268     /** @ignore */
58269     handleMouseDown : function(e, t){
58270         var v = this.grid.getView();
58271         if(this.isLocked()){
58272             return;
58273         };
58274         var row = v.findRowIndex(t);
58275         var cell = v.findCellIndex(t);
58276         if(row !== false && cell !== false){
58277             this.select(row, cell);
58278         }
58279     },
58280
58281     /**
58282      * Selects a cell.
58283      * @param {Number} rowIndex
58284      * @param {Number} collIndex
58285      */
58286     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58287         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58288             this.clearSelections();
58289             r = r || this.grid.dataSource.getAt(rowIndex);
58290             this.selection = {
58291                 record : r,
58292                 cell : [rowIndex, colIndex]
58293             };
58294             if(!preventViewNotify){
58295                 var v = this.grid.getView();
58296                 v.onCellSelect(rowIndex, colIndex);
58297                 if(preventFocus !== true){
58298                     v.focusCell(rowIndex, colIndex);
58299                 }
58300             }
58301             this.fireEvent("cellselect", this, rowIndex, colIndex);
58302             this.fireEvent("selectionchange", this, this.selection);
58303         }
58304     },
58305
58306         //private
58307     isSelectable : function(rowIndex, colIndex, cm){
58308         return !cm.isHidden(colIndex);
58309     },
58310
58311     /** @ignore */
58312     handleKeyDown : function(e){
58313         //Roo.log('Cell Sel Model handleKeyDown');
58314         if(!e.isNavKeyPress()){
58315             return;
58316         }
58317         var g = this.grid, s = this.selection;
58318         if(!s){
58319             e.stopEvent();
58320             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58321             if(cell){
58322                 this.select(cell[0], cell[1]);
58323             }
58324             return;
58325         }
58326         var sm = this;
58327         var walk = function(row, col, step){
58328             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58329         };
58330         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58331         var newCell;
58332
58333       
58334
58335         switch(k){
58336             case e.TAB:
58337                 // handled by onEditorKey
58338                 if (g.isEditor && g.editing) {
58339                     return;
58340                 }
58341                 if(e.shiftKey) {
58342                     newCell = walk(r, c-1, -1);
58343                 } else {
58344                     newCell = walk(r, c+1, 1);
58345                 }
58346                 break;
58347             
58348             case e.DOWN:
58349                newCell = walk(r+1, c, 1);
58350                 break;
58351             
58352             case e.UP:
58353                 newCell = walk(r-1, c, -1);
58354                 break;
58355             
58356             case e.RIGHT:
58357                 newCell = walk(r, c+1, 1);
58358                 break;
58359             
58360             case e.LEFT:
58361                 newCell = walk(r, c-1, -1);
58362                 break;
58363             
58364             case e.ENTER:
58365                 
58366                 if(g.isEditor && !g.editing){
58367                    g.startEditing(r, c);
58368                    e.stopEvent();
58369                    return;
58370                 }
58371                 
58372                 
58373              break;
58374         };
58375         if(newCell){
58376             this.select(newCell[0], newCell[1]);
58377             e.stopEvent();
58378             
58379         }
58380     },
58381
58382     acceptsNav : function(row, col, cm){
58383         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58384     },
58385     /**
58386      * Selects a cell.
58387      * @param {Number} field (not used) - as it's normally used as a listener
58388      * @param {Number} e - event - fake it by using
58389      *
58390      * var e = Roo.EventObjectImpl.prototype;
58391      * e.keyCode = e.TAB
58392      *
58393      * 
58394      */
58395     onEditorKey : function(field, e){
58396         
58397         var k = e.getKey(),
58398             newCell,
58399             g = this.grid,
58400             ed = g.activeEditor,
58401             forward = false;
58402         ///Roo.log('onEditorKey' + k);
58403         
58404         
58405         if (this.enter_is_tab && k == e.ENTER) {
58406             k = e.TAB;
58407         }
58408         
58409         if(k == e.TAB){
58410             if(e.shiftKey){
58411                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58412             }else{
58413                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58414                 forward = true;
58415             }
58416             
58417             e.stopEvent();
58418             
58419         } else if(k == e.ENTER &&  !e.ctrlKey){
58420             ed.completeEdit();
58421             e.stopEvent();
58422             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58423         
58424                 } else if(k == e.ESC){
58425             ed.cancelEdit();
58426         }
58427                 
58428         if (newCell) {
58429             var ecall = { cell : newCell, forward : forward };
58430             this.fireEvent('beforeeditnext', ecall );
58431             newCell = ecall.cell;
58432                         forward = ecall.forward;
58433         }
58434                 
58435         if(newCell){
58436             //Roo.log('next cell after edit');
58437             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58438         } else if (forward) {
58439             // tabbed past last
58440             this.fireEvent.defer(100, this, ['tabend',this]);
58441         }
58442     }
58443 });/*
58444  * Based on:
58445  * Ext JS Library 1.1.1
58446  * Copyright(c) 2006-2007, Ext JS, LLC.
58447  *
58448  * Originally Released Under LGPL - original licence link has changed is not relivant.
58449  *
58450  * Fork - LGPL
58451  * <script type="text/javascript">
58452  */
58453  
58454 /**
58455  * @class Roo.grid.EditorGrid
58456  * @extends Roo.grid.Grid
58457  * Class for creating and editable grid.
58458  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58459  * The container MUST have some type of size defined for the grid to fill. The container will be 
58460  * automatically set to position relative if it isn't already.
58461  * @param {Object} dataSource The data model to bind to
58462  * @param {Object} colModel The column model with info about this grid's columns
58463  */
58464 Roo.grid.EditorGrid = function(container, config){
58465     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58466     this.getGridEl().addClass("xedit-grid");
58467
58468     if(!this.selModel){
58469         this.selModel = new Roo.grid.CellSelectionModel();
58470     }
58471
58472     this.activeEditor = null;
58473
58474         this.addEvents({
58475             /**
58476              * @event beforeedit
58477              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58478              * <ul style="padding:5px;padding-left:16px;">
58479              * <li>grid - This grid</li>
58480              * <li>record - The record being edited</li>
58481              * <li>field - The field name being edited</li>
58482              * <li>value - The value for the field being edited.</li>
58483              * <li>row - The grid row index</li>
58484              * <li>column - The grid column index</li>
58485              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58486              * </ul>
58487              * @param {Object} e An edit event (see above for description)
58488              */
58489             "beforeedit" : true,
58490             /**
58491              * @event afteredit
58492              * Fires after a cell is edited. <br />
58493              * <ul style="padding:5px;padding-left:16px;">
58494              * <li>grid - This grid</li>
58495              * <li>record - The record being edited</li>
58496              * <li>field - The field name being edited</li>
58497              * <li>value - The value being set</li>
58498              * <li>originalValue - The original value for the field, before the edit.</li>
58499              * <li>row - The grid row index</li>
58500              * <li>column - The grid column index</li>
58501              * </ul>
58502              * @param {Object} e An edit event (see above for description)
58503              */
58504             "afteredit" : true,
58505             /**
58506              * @event validateedit
58507              * Fires after a cell is edited, but before the value is set in the record. 
58508          * You can use this to modify the value being set in the field, Return false
58509              * to cancel the change. The edit event object has the following properties <br />
58510              * <ul style="padding:5px;padding-left:16px;">
58511          * <li>editor - This editor</li>
58512              * <li>grid - This grid</li>
58513              * <li>record - The record being edited</li>
58514              * <li>field - The field name being edited</li>
58515              * <li>value - The value being set</li>
58516              * <li>originalValue - The original value for the field, before the edit.</li>
58517              * <li>row - The grid row index</li>
58518              * <li>column - The grid column index</li>
58519              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58520              * </ul>
58521              * @param {Object} e An edit event (see above for description)
58522              */
58523             "validateedit" : true
58524         });
58525     this.on("bodyscroll", this.stopEditing,  this);
58526     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58527 };
58528
58529 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58530     /**
58531      * @cfg {Number} clicksToEdit
58532      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58533      */
58534     clicksToEdit: 2,
58535
58536     // private
58537     isEditor : true,
58538     // private
58539     trackMouseOver: false, // causes very odd FF errors
58540
58541     onCellDblClick : function(g, row, col){
58542         this.startEditing(row, col);
58543     },
58544
58545     onEditComplete : function(ed, value, startValue){
58546         this.editing = false;
58547         this.activeEditor = null;
58548         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58549         var r = ed.record;
58550         var field = this.colModel.getDataIndex(ed.col);
58551         var e = {
58552             grid: this,
58553             record: r,
58554             field: field,
58555             originalValue: startValue,
58556             value: value,
58557             row: ed.row,
58558             column: ed.col,
58559             cancel:false,
58560             editor: ed
58561         };
58562         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58563         cell.show();
58564           
58565         if(String(value) !== String(startValue)){
58566             
58567             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58568                 r.set(field, e.value);
58569                 // if we are dealing with a combo box..
58570                 // then we also set the 'name' colum to be the displayField
58571                 if (ed.field.displayField && ed.field.name) {
58572                     r.set(ed.field.name, ed.field.el.dom.value);
58573                 }
58574                 
58575                 delete e.cancel; //?? why!!!
58576                 this.fireEvent("afteredit", e);
58577             }
58578         } else {
58579             this.fireEvent("afteredit", e); // always fire it!
58580         }
58581         this.view.focusCell(ed.row, ed.col);
58582     },
58583
58584     /**
58585      * Starts editing the specified for the specified row/column
58586      * @param {Number} rowIndex
58587      * @param {Number} colIndex
58588      */
58589     startEditing : function(row, col){
58590         this.stopEditing();
58591         if(this.colModel.isCellEditable(col, row)){
58592             this.view.ensureVisible(row, col, true);
58593           
58594             var r = this.dataSource.getAt(row);
58595             var field = this.colModel.getDataIndex(col);
58596             var cell = Roo.get(this.view.getCell(row,col));
58597             var e = {
58598                 grid: this,
58599                 record: r,
58600                 field: field,
58601                 value: r.data[field],
58602                 row: row,
58603                 column: col,
58604                 cancel:false 
58605             };
58606             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58607                 this.editing = true;
58608                 var ed = this.colModel.getCellEditor(col, row);
58609                 
58610                 if (!ed) {
58611                     return;
58612                 }
58613                 if(!ed.rendered){
58614                     ed.render(ed.parentEl || document.body);
58615                 }
58616                 ed.field.reset();
58617                
58618                 cell.hide();
58619                 
58620                 (function(){ // complex but required for focus issues in safari, ie and opera
58621                     ed.row = row;
58622                     ed.col = col;
58623                     ed.record = r;
58624                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58625                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58626                     this.activeEditor = ed;
58627                     var v = r.data[field];
58628                     ed.startEdit(this.view.getCell(row, col), v);
58629                     // combo's with 'displayField and name set
58630                     if (ed.field.displayField && ed.field.name) {
58631                         ed.field.el.dom.value = r.data[ed.field.name];
58632                     }
58633                     
58634                     
58635                 }).defer(50, this);
58636             }
58637         }
58638     },
58639         
58640     /**
58641      * Stops any active editing
58642      */
58643     stopEditing : function(){
58644         if(this.activeEditor){
58645             this.activeEditor.completeEdit();
58646         }
58647         this.activeEditor = null;
58648     },
58649         
58650          /**
58651      * Called to get grid's drag proxy text, by default returns this.ddText.
58652      * @return {String}
58653      */
58654     getDragDropText : function(){
58655         var count = this.selModel.getSelectedCell() ? 1 : 0;
58656         return String.format(this.ddText, count, count == 1 ? '' : 's');
58657     }
58658         
58659 });/*
58660  * Based on:
58661  * Ext JS Library 1.1.1
58662  * Copyright(c) 2006-2007, Ext JS, LLC.
58663  *
58664  * Originally Released Under LGPL - original licence link has changed is not relivant.
58665  *
58666  * Fork - LGPL
58667  * <script type="text/javascript">
58668  */
58669
58670 // private - not really -- you end up using it !
58671 // This is a support class used internally by the Grid components
58672
58673 /**
58674  * @class Roo.grid.GridEditor
58675  * @extends Roo.Editor
58676  * Class for creating and editable grid elements.
58677  * @param {Object} config any settings (must include field)
58678  */
58679 Roo.grid.GridEditor = function(field, config){
58680     if (!config && field.field) {
58681         config = field;
58682         field = Roo.factory(config.field, Roo.form);
58683     }
58684     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58685     field.monitorTab = false;
58686 };
58687
58688 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58689     
58690     /**
58691      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58692      */
58693     
58694     alignment: "tl-tl",
58695     autoSize: "width",
58696     hideEl : false,
58697     cls: "x-small-editor x-grid-editor",
58698     shim:false,
58699     shadow:"frame"
58700 });/*
58701  * Based on:
58702  * Ext JS Library 1.1.1
58703  * Copyright(c) 2006-2007, Ext JS, LLC.
58704  *
58705  * Originally Released Under LGPL - original licence link has changed is not relivant.
58706  *
58707  * Fork - LGPL
58708  * <script type="text/javascript">
58709  */
58710   
58711
58712   
58713 Roo.grid.PropertyRecord = Roo.data.Record.create([
58714     {name:'name',type:'string'},  'value'
58715 ]);
58716
58717
58718 Roo.grid.PropertyStore = function(grid, source){
58719     this.grid = grid;
58720     this.store = new Roo.data.Store({
58721         recordType : Roo.grid.PropertyRecord
58722     });
58723     this.store.on('update', this.onUpdate,  this);
58724     if(source){
58725         this.setSource(source);
58726     }
58727     Roo.grid.PropertyStore.superclass.constructor.call(this);
58728 };
58729
58730
58731
58732 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58733     setSource : function(o){
58734         this.source = o;
58735         this.store.removeAll();
58736         var data = [];
58737         for(var k in o){
58738             if(this.isEditableValue(o[k])){
58739                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58740             }
58741         }
58742         this.store.loadRecords({records: data}, {}, true);
58743     },
58744
58745     onUpdate : function(ds, record, type){
58746         if(type == Roo.data.Record.EDIT){
58747             var v = record.data['value'];
58748             var oldValue = record.modified['value'];
58749             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58750                 this.source[record.id] = v;
58751                 record.commit();
58752                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58753             }else{
58754                 record.reject();
58755             }
58756         }
58757     },
58758
58759     getProperty : function(row){
58760        return this.store.getAt(row);
58761     },
58762
58763     isEditableValue: function(val){
58764         if(val && val instanceof Date){
58765             return true;
58766         }else if(typeof val == 'object' || typeof val == 'function'){
58767             return false;
58768         }
58769         return true;
58770     },
58771
58772     setValue : function(prop, value){
58773         this.source[prop] = value;
58774         this.store.getById(prop).set('value', value);
58775     },
58776
58777     getSource : function(){
58778         return this.source;
58779     }
58780 });
58781
58782 Roo.grid.PropertyColumnModel = function(grid, store){
58783     this.grid = grid;
58784     var g = Roo.grid;
58785     g.PropertyColumnModel.superclass.constructor.call(this, [
58786         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58787         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58788     ]);
58789     this.store = store;
58790     this.bselect = Roo.DomHelper.append(document.body, {
58791         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58792             {tag: 'option', value: 'true', html: 'true'},
58793             {tag: 'option', value: 'false', html: 'false'}
58794         ]
58795     });
58796     Roo.id(this.bselect);
58797     var f = Roo.form;
58798     this.editors = {
58799         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58800         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58801         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58802         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58803         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58804     };
58805     this.renderCellDelegate = this.renderCell.createDelegate(this);
58806     this.renderPropDelegate = this.renderProp.createDelegate(this);
58807 };
58808
58809 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58810     
58811     
58812     nameText : 'Name',
58813     valueText : 'Value',
58814     
58815     dateFormat : 'm/j/Y',
58816     
58817     
58818     renderDate : function(dateVal){
58819         return dateVal.dateFormat(this.dateFormat);
58820     },
58821
58822     renderBool : function(bVal){
58823         return bVal ? 'true' : 'false';
58824     },
58825
58826     isCellEditable : function(colIndex, rowIndex){
58827         return colIndex == 1;
58828     },
58829
58830     getRenderer : function(col){
58831         return col == 1 ?
58832             this.renderCellDelegate : this.renderPropDelegate;
58833     },
58834
58835     renderProp : function(v){
58836         return this.getPropertyName(v);
58837     },
58838
58839     renderCell : function(val){
58840         var rv = val;
58841         if(val instanceof Date){
58842             rv = this.renderDate(val);
58843         }else if(typeof val == 'boolean'){
58844             rv = this.renderBool(val);
58845         }
58846         return Roo.util.Format.htmlEncode(rv);
58847     },
58848
58849     getPropertyName : function(name){
58850         var pn = this.grid.propertyNames;
58851         return pn && pn[name] ? pn[name] : name;
58852     },
58853
58854     getCellEditor : function(colIndex, rowIndex){
58855         var p = this.store.getProperty(rowIndex);
58856         var n = p.data['name'], val = p.data['value'];
58857         
58858         if(typeof(this.grid.customEditors[n]) == 'string'){
58859             return this.editors[this.grid.customEditors[n]];
58860         }
58861         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58862             return this.grid.customEditors[n];
58863         }
58864         if(val instanceof Date){
58865             return this.editors['date'];
58866         }else if(typeof val == 'number'){
58867             return this.editors['number'];
58868         }else if(typeof val == 'boolean'){
58869             return this.editors['boolean'];
58870         }else{
58871             return this.editors['string'];
58872         }
58873     }
58874 });
58875
58876 /**
58877  * @class Roo.grid.PropertyGrid
58878  * @extends Roo.grid.EditorGrid
58879  * This class represents the  interface of a component based property grid control.
58880  * <br><br>Usage:<pre><code>
58881  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58882       
58883  });
58884  // set any options
58885  grid.render();
58886  * </code></pre>
58887   
58888  * @constructor
58889  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58890  * The container MUST have some type of size defined for the grid to fill. The container will be
58891  * automatically set to position relative if it isn't already.
58892  * @param {Object} config A config object that sets properties on this grid.
58893  */
58894 Roo.grid.PropertyGrid = function(container, config){
58895     config = config || {};
58896     var store = new Roo.grid.PropertyStore(this);
58897     this.store = store;
58898     var cm = new Roo.grid.PropertyColumnModel(this, store);
58899     store.store.sort('name', 'ASC');
58900     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58901         ds: store.store,
58902         cm: cm,
58903         enableColLock:false,
58904         enableColumnMove:false,
58905         stripeRows:false,
58906         trackMouseOver: false,
58907         clicksToEdit:1
58908     }, config));
58909     this.getGridEl().addClass('x-props-grid');
58910     this.lastEditRow = null;
58911     this.on('columnresize', this.onColumnResize, this);
58912     this.addEvents({
58913          /**
58914              * @event beforepropertychange
58915              * Fires before a property changes (return false to stop?)
58916              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58917              * @param {String} id Record Id
58918              * @param {String} newval New Value
58919          * @param {String} oldval Old Value
58920              */
58921         "beforepropertychange": true,
58922         /**
58923              * @event propertychange
58924              * Fires after a property changes
58925              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58926              * @param {String} id Record Id
58927              * @param {String} newval New Value
58928          * @param {String} oldval Old Value
58929              */
58930         "propertychange": true
58931     });
58932     this.customEditors = this.customEditors || {};
58933 };
58934 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58935     
58936      /**
58937      * @cfg {Object} customEditors map of colnames=> custom editors.
58938      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58939      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58940      * false disables editing of the field.
58941          */
58942     
58943       /**
58944      * @cfg {Object} propertyNames map of property Names to their displayed value
58945          */
58946     
58947     render : function(){
58948         Roo.grid.PropertyGrid.superclass.render.call(this);
58949         this.autoSize.defer(100, this);
58950     },
58951
58952     autoSize : function(){
58953         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58954         if(this.view){
58955             this.view.fitColumns();
58956         }
58957     },
58958
58959     onColumnResize : function(){
58960         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58961         this.autoSize();
58962     },
58963     /**
58964      * Sets the data for the Grid
58965      * accepts a Key => Value object of all the elements avaiable.
58966      * @param {Object} data  to appear in grid.
58967      */
58968     setSource : function(source){
58969         this.store.setSource(source);
58970         //this.autoSize();
58971     },
58972     /**
58973      * Gets all the data from the grid.
58974      * @return {Object} data  data stored in grid
58975      */
58976     getSource : function(){
58977         return this.store.getSource();
58978     }
58979 });/*
58980   
58981  * Licence LGPL
58982  
58983  */
58984  
58985 /**
58986  * @class Roo.grid.Calendar
58987  * @extends Roo.util.Grid
58988  * This class extends the Grid to provide a calendar widget
58989  * <br><br>Usage:<pre><code>
58990  var grid = new Roo.grid.Calendar("my-container-id", {
58991      ds: myDataStore,
58992      cm: myColModel,
58993      selModel: mySelectionModel,
58994      autoSizeColumns: true,
58995      monitorWindowResize: false,
58996      trackMouseOver: true
58997      eventstore : real data store..
58998  });
58999  // set any options
59000  grid.render();
59001   
59002   * @constructor
59003  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59004  * The container MUST have some type of size defined for the grid to fill. The container will be
59005  * automatically set to position relative if it isn't already.
59006  * @param {Object} config A config object that sets properties on this grid.
59007  */
59008 Roo.grid.Calendar = function(container, config){
59009         // initialize the container
59010         this.container = Roo.get(container);
59011         this.container.update("");
59012         this.container.setStyle("overflow", "hidden");
59013     this.container.addClass('x-grid-container');
59014
59015     this.id = this.container.id;
59016
59017     Roo.apply(this, config);
59018     // check and correct shorthanded configs
59019     
59020     var rows = [];
59021     var d =1;
59022     for (var r = 0;r < 6;r++) {
59023         
59024         rows[r]=[];
59025         for (var c =0;c < 7;c++) {
59026             rows[r][c]= '';
59027         }
59028     }
59029     if (this.eventStore) {
59030         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59031         this.eventStore.on('load',this.onLoad, this);
59032         this.eventStore.on('beforeload',this.clearEvents, this);
59033          
59034     }
59035     
59036     this.dataSource = new Roo.data.Store({
59037             proxy: new Roo.data.MemoryProxy(rows),
59038             reader: new Roo.data.ArrayReader({}, [
59039                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59040     });
59041
59042     this.dataSource.load();
59043     this.ds = this.dataSource;
59044     this.ds.xmodule = this.xmodule || false;
59045     
59046     
59047     var cellRender = function(v,x,r)
59048     {
59049         return String.format(
59050             '<div class="fc-day  fc-widget-content"><div>' +
59051                 '<div class="fc-event-container"></div>' +
59052                 '<div class="fc-day-number">{0}</div>'+
59053                 
59054                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59055             '</div></div>', v);
59056     
59057     }
59058     
59059     
59060     this.colModel = new Roo.grid.ColumnModel( [
59061         {
59062             xtype: 'ColumnModel',
59063             xns: Roo.grid,
59064             dataIndex : 'weekday0',
59065             header : 'Sunday',
59066             renderer : cellRender
59067         },
59068         {
59069             xtype: 'ColumnModel',
59070             xns: Roo.grid,
59071             dataIndex : 'weekday1',
59072             header : 'Monday',
59073             renderer : cellRender
59074         },
59075         {
59076             xtype: 'ColumnModel',
59077             xns: Roo.grid,
59078             dataIndex : 'weekday2',
59079             header : 'Tuesday',
59080             renderer : cellRender
59081         },
59082         {
59083             xtype: 'ColumnModel',
59084             xns: Roo.grid,
59085             dataIndex : 'weekday3',
59086             header : 'Wednesday',
59087             renderer : cellRender
59088         },
59089         {
59090             xtype: 'ColumnModel',
59091             xns: Roo.grid,
59092             dataIndex : 'weekday4',
59093             header : 'Thursday',
59094             renderer : cellRender
59095         },
59096         {
59097             xtype: 'ColumnModel',
59098             xns: Roo.grid,
59099             dataIndex : 'weekday5',
59100             header : 'Friday',
59101             renderer : cellRender
59102         },
59103         {
59104             xtype: 'ColumnModel',
59105             xns: Roo.grid,
59106             dataIndex : 'weekday6',
59107             header : 'Saturday',
59108             renderer : cellRender
59109         }
59110     ]);
59111     this.cm = this.colModel;
59112     this.cm.xmodule = this.xmodule || false;
59113  
59114         
59115           
59116     //this.selModel = new Roo.grid.CellSelectionModel();
59117     //this.sm = this.selModel;
59118     //this.selModel.init(this);
59119     
59120     
59121     if(this.width){
59122         this.container.setWidth(this.width);
59123     }
59124
59125     if(this.height){
59126         this.container.setHeight(this.height);
59127     }
59128     /** @private */
59129         this.addEvents({
59130         // raw events
59131         /**
59132          * @event click
59133          * The raw click event for the entire grid.
59134          * @param {Roo.EventObject} e
59135          */
59136         "click" : true,
59137         /**
59138          * @event dblclick
59139          * The raw dblclick event for the entire grid.
59140          * @param {Roo.EventObject} e
59141          */
59142         "dblclick" : true,
59143         /**
59144          * @event contextmenu
59145          * The raw contextmenu event for the entire grid.
59146          * @param {Roo.EventObject} e
59147          */
59148         "contextmenu" : true,
59149         /**
59150          * @event mousedown
59151          * The raw mousedown event for the entire grid.
59152          * @param {Roo.EventObject} e
59153          */
59154         "mousedown" : true,
59155         /**
59156          * @event mouseup
59157          * The raw mouseup event for the entire grid.
59158          * @param {Roo.EventObject} e
59159          */
59160         "mouseup" : true,
59161         /**
59162          * @event mouseover
59163          * The raw mouseover event for the entire grid.
59164          * @param {Roo.EventObject} e
59165          */
59166         "mouseover" : true,
59167         /**
59168          * @event mouseout
59169          * The raw mouseout event for the entire grid.
59170          * @param {Roo.EventObject} e
59171          */
59172         "mouseout" : true,
59173         /**
59174          * @event keypress
59175          * The raw keypress event for the entire grid.
59176          * @param {Roo.EventObject} e
59177          */
59178         "keypress" : true,
59179         /**
59180          * @event keydown
59181          * The raw keydown event for the entire grid.
59182          * @param {Roo.EventObject} e
59183          */
59184         "keydown" : true,
59185
59186         // custom events
59187
59188         /**
59189          * @event cellclick
59190          * Fires when a cell is clicked
59191          * @param {Grid} this
59192          * @param {Number} rowIndex
59193          * @param {Number} columnIndex
59194          * @param {Roo.EventObject} e
59195          */
59196         "cellclick" : true,
59197         /**
59198          * @event celldblclick
59199          * Fires when a cell is double clicked
59200          * @param {Grid} this
59201          * @param {Number} rowIndex
59202          * @param {Number} columnIndex
59203          * @param {Roo.EventObject} e
59204          */
59205         "celldblclick" : true,
59206         /**
59207          * @event rowclick
59208          * Fires when a row is clicked
59209          * @param {Grid} this
59210          * @param {Number} rowIndex
59211          * @param {Roo.EventObject} e
59212          */
59213         "rowclick" : true,
59214         /**
59215          * @event rowdblclick
59216          * Fires when a row is double clicked
59217          * @param {Grid} this
59218          * @param {Number} rowIndex
59219          * @param {Roo.EventObject} e
59220          */
59221         "rowdblclick" : true,
59222         /**
59223          * @event headerclick
59224          * Fires when a header is clicked
59225          * @param {Grid} this
59226          * @param {Number} columnIndex
59227          * @param {Roo.EventObject} e
59228          */
59229         "headerclick" : true,
59230         /**
59231          * @event headerdblclick
59232          * Fires when a header cell is double clicked
59233          * @param {Grid} this
59234          * @param {Number} columnIndex
59235          * @param {Roo.EventObject} e
59236          */
59237         "headerdblclick" : true,
59238         /**
59239          * @event rowcontextmenu
59240          * Fires when a row is right clicked
59241          * @param {Grid} this
59242          * @param {Number} rowIndex
59243          * @param {Roo.EventObject} e
59244          */
59245         "rowcontextmenu" : true,
59246         /**
59247          * @event cellcontextmenu
59248          * Fires when a cell is right clicked
59249          * @param {Grid} this
59250          * @param {Number} rowIndex
59251          * @param {Number} cellIndex
59252          * @param {Roo.EventObject} e
59253          */
59254          "cellcontextmenu" : true,
59255         /**
59256          * @event headercontextmenu
59257          * Fires when a header is right clicked
59258          * @param {Grid} this
59259          * @param {Number} columnIndex
59260          * @param {Roo.EventObject} e
59261          */
59262         "headercontextmenu" : true,
59263         /**
59264          * @event bodyscroll
59265          * Fires when the body element is scrolled
59266          * @param {Number} scrollLeft
59267          * @param {Number} scrollTop
59268          */
59269         "bodyscroll" : true,
59270         /**
59271          * @event columnresize
59272          * Fires when the user resizes a column
59273          * @param {Number} columnIndex
59274          * @param {Number} newSize
59275          */
59276         "columnresize" : true,
59277         /**
59278          * @event columnmove
59279          * Fires when the user moves a column
59280          * @param {Number} oldIndex
59281          * @param {Number} newIndex
59282          */
59283         "columnmove" : true,
59284         /**
59285          * @event startdrag
59286          * Fires when row(s) start being dragged
59287          * @param {Grid} this
59288          * @param {Roo.GridDD} dd The drag drop object
59289          * @param {event} e The raw browser event
59290          */
59291         "startdrag" : true,
59292         /**
59293          * @event enddrag
59294          * Fires when a drag operation is complete
59295          * @param {Grid} this
59296          * @param {Roo.GridDD} dd The drag drop object
59297          * @param {event} e The raw browser event
59298          */
59299         "enddrag" : true,
59300         /**
59301          * @event dragdrop
59302          * Fires when dragged row(s) are dropped on a valid DD target
59303          * @param {Grid} this
59304          * @param {Roo.GridDD} dd The drag drop object
59305          * @param {String} targetId The target drag drop object
59306          * @param {event} e The raw browser event
59307          */
59308         "dragdrop" : true,
59309         /**
59310          * @event dragover
59311          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59312          * @param {Grid} this
59313          * @param {Roo.GridDD} dd The drag drop object
59314          * @param {String} targetId The target drag drop object
59315          * @param {event} e The raw browser event
59316          */
59317         "dragover" : true,
59318         /**
59319          * @event dragenter
59320          *  Fires when the dragged row(s) first cross another DD target while being dragged
59321          * @param {Grid} this
59322          * @param {Roo.GridDD} dd The drag drop object
59323          * @param {String} targetId The target drag drop object
59324          * @param {event} e The raw browser event
59325          */
59326         "dragenter" : true,
59327         /**
59328          * @event dragout
59329          * Fires when the dragged row(s) leave another DD target while being dragged
59330          * @param {Grid} this
59331          * @param {Roo.GridDD} dd The drag drop object
59332          * @param {String} targetId The target drag drop object
59333          * @param {event} e The raw browser event
59334          */
59335         "dragout" : true,
59336         /**
59337          * @event rowclass
59338          * Fires when a row is rendered, so you can change add a style to it.
59339          * @param {GridView} gridview   The grid view
59340          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59341          */
59342         'rowclass' : true,
59343
59344         /**
59345          * @event render
59346          * Fires when the grid is rendered
59347          * @param {Grid} grid
59348          */
59349         'render' : true,
59350             /**
59351              * @event select
59352              * Fires when a date is selected
59353              * @param {DatePicker} this
59354              * @param {Date} date The selected date
59355              */
59356         'select': true,
59357         /**
59358              * @event monthchange
59359              * Fires when the displayed month changes 
59360              * @param {DatePicker} this
59361              * @param {Date} date The selected month
59362              */
59363         'monthchange': true,
59364         /**
59365              * @event evententer
59366              * Fires when mouse over an event
59367              * @param {Calendar} this
59368              * @param {event} Event
59369              */
59370         'evententer': true,
59371         /**
59372              * @event eventleave
59373              * Fires when the mouse leaves an
59374              * @param {Calendar} this
59375              * @param {event}
59376              */
59377         'eventleave': true,
59378         /**
59379              * @event eventclick
59380              * Fires when the mouse click an
59381              * @param {Calendar} this
59382              * @param {event}
59383              */
59384         'eventclick': true,
59385         /**
59386              * @event eventrender
59387              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59388              * @param {Calendar} this
59389              * @param {data} data to be modified
59390              */
59391         'eventrender': true
59392         
59393     });
59394
59395     Roo.grid.Grid.superclass.constructor.call(this);
59396     this.on('render', function() {
59397         this.view.el.addClass('x-grid-cal'); 
59398         
59399         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59400
59401     },this);
59402     
59403     if (!Roo.grid.Calendar.style) {
59404         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59405             
59406             
59407             '.x-grid-cal .x-grid-col' :  {
59408                 height: 'auto !important',
59409                 'vertical-align': 'top'
59410             },
59411             '.x-grid-cal  .fc-event-hori' : {
59412                 height: '14px'
59413             }
59414              
59415             
59416         }, Roo.id());
59417     }
59418
59419     
59420     
59421 };
59422 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59423     /**
59424      * @cfg {Store} eventStore The store that loads events.
59425      */
59426     eventStore : 25,
59427
59428      
59429     activeDate : false,
59430     startDay : 0,
59431     autoWidth : true,
59432     monitorWindowResize : false,
59433
59434     
59435     resizeColumns : function() {
59436         var col = (this.view.el.getWidth() / 7) - 3;
59437         // loop through cols, and setWidth
59438         for(var i =0 ; i < 7 ; i++){
59439             this.cm.setColumnWidth(i, col);
59440         }
59441     },
59442      setDate :function(date) {
59443         
59444         Roo.log('setDate?');
59445         
59446         this.resizeColumns();
59447         var vd = this.activeDate;
59448         this.activeDate = date;
59449 //        if(vd && this.el){
59450 //            var t = date.getTime();
59451 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59452 //                Roo.log('using add remove');
59453 //                
59454 //                this.fireEvent('monthchange', this, date);
59455 //                
59456 //                this.cells.removeClass("fc-state-highlight");
59457 //                this.cells.each(function(c){
59458 //                   if(c.dateValue == t){
59459 //                       c.addClass("fc-state-highlight");
59460 //                       setTimeout(function(){
59461 //                            try{c.dom.firstChild.focus();}catch(e){}
59462 //                       }, 50);
59463 //                       return false;
59464 //                   }
59465 //                   return true;
59466 //                });
59467 //                return;
59468 //            }
59469 //        }
59470         
59471         var days = date.getDaysInMonth();
59472         
59473         var firstOfMonth = date.getFirstDateOfMonth();
59474         var startingPos = firstOfMonth.getDay()-this.startDay;
59475         
59476         if(startingPos < this.startDay){
59477             startingPos += 7;
59478         }
59479         
59480         var pm = date.add(Date.MONTH, -1);
59481         var prevStart = pm.getDaysInMonth()-startingPos;
59482 //        
59483         
59484         
59485         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59486         
59487         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59488         //this.cells.addClassOnOver('fc-state-hover');
59489         
59490         var cells = this.cells.elements;
59491         var textEls = this.textNodes;
59492         
59493         //Roo.each(cells, function(cell){
59494         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59495         //});
59496         
59497         days += startingPos;
59498
59499         // convert everything to numbers so it's fast
59500         var day = 86400000;
59501         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59502         //Roo.log(d);
59503         //Roo.log(pm);
59504         //Roo.log(prevStart);
59505         
59506         var today = new Date().clearTime().getTime();
59507         var sel = date.clearTime().getTime();
59508         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59509         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59510         var ddMatch = this.disabledDatesRE;
59511         var ddText = this.disabledDatesText;
59512         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59513         var ddaysText = this.disabledDaysText;
59514         var format = this.format;
59515         
59516         var setCellClass = function(cal, cell){
59517             
59518             //Roo.log('set Cell Class');
59519             cell.title = "";
59520             var t = d.getTime();
59521             
59522             //Roo.log(d);
59523             
59524             
59525             cell.dateValue = t;
59526             if(t == today){
59527                 cell.className += " fc-today";
59528                 cell.className += " fc-state-highlight";
59529                 cell.title = cal.todayText;
59530             }
59531             if(t == sel){
59532                 // disable highlight in other month..
59533                 cell.className += " fc-state-highlight";
59534                 
59535             }
59536             // disabling
59537             if(t < min) {
59538                 //cell.className = " fc-state-disabled";
59539                 cell.title = cal.minText;
59540                 return;
59541             }
59542             if(t > max) {
59543                 //cell.className = " fc-state-disabled";
59544                 cell.title = cal.maxText;
59545                 return;
59546             }
59547             if(ddays){
59548                 if(ddays.indexOf(d.getDay()) != -1){
59549                     // cell.title = ddaysText;
59550                    // cell.className = " fc-state-disabled";
59551                 }
59552             }
59553             if(ddMatch && format){
59554                 var fvalue = d.dateFormat(format);
59555                 if(ddMatch.test(fvalue)){
59556                     cell.title = ddText.replace("%0", fvalue);
59557                    cell.className = " fc-state-disabled";
59558                 }
59559             }
59560             
59561             if (!cell.initialClassName) {
59562                 cell.initialClassName = cell.dom.className;
59563             }
59564             
59565             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59566         };
59567
59568         var i = 0;
59569         
59570         for(; i < startingPos; i++) {
59571             cells[i].dayName =  (++prevStart);
59572             Roo.log(textEls[i]);
59573             d.setDate(d.getDate()+1);
59574             
59575             //cells[i].className = "fc-past fc-other-month";
59576             setCellClass(this, cells[i]);
59577         }
59578         
59579         var intDay = 0;
59580         
59581         for(; i < days; i++){
59582             intDay = i - startingPos + 1;
59583             cells[i].dayName =  (intDay);
59584             d.setDate(d.getDate()+1);
59585             
59586             cells[i].className = ''; // "x-date-active";
59587             setCellClass(this, cells[i]);
59588         }
59589         var extraDays = 0;
59590         
59591         for(; i < 42; i++) {
59592             //textEls[i].innerHTML = (++extraDays);
59593             
59594             d.setDate(d.getDate()+1);
59595             cells[i].dayName = (++extraDays);
59596             cells[i].className = "fc-future fc-other-month";
59597             setCellClass(this, cells[i]);
59598         }
59599         
59600         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59601         
59602         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59603         
59604         // this will cause all the cells to mis
59605         var rows= [];
59606         var i =0;
59607         for (var r = 0;r < 6;r++) {
59608             for (var c =0;c < 7;c++) {
59609                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59610             }    
59611         }
59612         
59613         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59614         for(i=0;i<cells.length;i++) {
59615             
59616             this.cells.elements[i].dayName = cells[i].dayName ;
59617             this.cells.elements[i].className = cells[i].className;
59618             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59619             this.cells.elements[i].title = cells[i].title ;
59620             this.cells.elements[i].dateValue = cells[i].dateValue ;
59621         }
59622         
59623         
59624         
59625         
59626         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59627         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59628         
59629         ////if(totalRows != 6){
59630             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59631            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59632        // }
59633         
59634         this.fireEvent('monthchange', this, date);
59635         
59636         
59637     },
59638  /**
59639      * Returns the grid's SelectionModel.
59640      * @return {SelectionModel}
59641      */
59642     getSelectionModel : function(){
59643         if(!this.selModel){
59644             this.selModel = new Roo.grid.CellSelectionModel();
59645         }
59646         return this.selModel;
59647     },
59648
59649     load: function() {
59650         this.eventStore.load()
59651         
59652         
59653         
59654     },
59655     
59656     findCell : function(dt) {
59657         dt = dt.clearTime().getTime();
59658         var ret = false;
59659         this.cells.each(function(c){
59660             //Roo.log("check " +c.dateValue + '?=' + dt);
59661             if(c.dateValue == dt){
59662                 ret = c;
59663                 return false;
59664             }
59665             return true;
59666         });
59667         
59668         return ret;
59669     },
59670     
59671     findCells : function(rec) {
59672         var s = rec.data.start_dt.clone().clearTime().getTime();
59673        // Roo.log(s);
59674         var e= rec.data.end_dt.clone().clearTime().getTime();
59675        // Roo.log(e);
59676         var ret = [];
59677         this.cells.each(function(c){
59678              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59679             
59680             if(c.dateValue > e){
59681                 return ;
59682             }
59683             if(c.dateValue < s){
59684                 return ;
59685             }
59686             ret.push(c);
59687         });
59688         
59689         return ret;    
59690     },
59691     
59692     findBestRow: function(cells)
59693     {
59694         var ret = 0;
59695         
59696         for (var i =0 ; i < cells.length;i++) {
59697             ret  = Math.max(cells[i].rows || 0,ret);
59698         }
59699         return ret;
59700         
59701     },
59702     
59703     
59704     addItem : function(rec)
59705     {
59706         // look for vertical location slot in
59707         var cells = this.findCells(rec);
59708         
59709         rec.row = this.findBestRow(cells);
59710         
59711         // work out the location.
59712         
59713         var crow = false;
59714         var rows = [];
59715         for(var i =0; i < cells.length; i++) {
59716             if (!crow) {
59717                 crow = {
59718                     start : cells[i],
59719                     end :  cells[i]
59720                 };
59721                 continue;
59722             }
59723             if (crow.start.getY() == cells[i].getY()) {
59724                 // on same row.
59725                 crow.end = cells[i];
59726                 continue;
59727             }
59728             // different row.
59729             rows.push(crow);
59730             crow = {
59731                 start: cells[i],
59732                 end : cells[i]
59733             };
59734             
59735         }
59736         
59737         rows.push(crow);
59738         rec.els = [];
59739         rec.rows = rows;
59740         rec.cells = cells;
59741         for (var i = 0; i < cells.length;i++) {
59742             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59743             
59744         }
59745         
59746         
59747     },
59748     
59749     clearEvents: function() {
59750         
59751         if (!this.eventStore.getCount()) {
59752             return;
59753         }
59754         // reset number of rows in cells.
59755         Roo.each(this.cells.elements, function(c){
59756             c.rows = 0;
59757         });
59758         
59759         this.eventStore.each(function(e) {
59760             this.clearEvent(e);
59761         },this);
59762         
59763     },
59764     
59765     clearEvent : function(ev)
59766     {
59767         if (ev.els) {
59768             Roo.each(ev.els, function(el) {
59769                 el.un('mouseenter' ,this.onEventEnter, this);
59770                 el.un('mouseleave' ,this.onEventLeave, this);
59771                 el.remove();
59772             },this);
59773             ev.els = [];
59774         }
59775     },
59776     
59777     
59778     renderEvent : function(ev,ctr) {
59779         if (!ctr) {
59780              ctr = this.view.el.select('.fc-event-container',true).first();
59781         }
59782         
59783          
59784         this.clearEvent(ev);
59785             //code
59786        
59787         
59788         
59789         ev.els = [];
59790         var cells = ev.cells;
59791         var rows = ev.rows;
59792         this.fireEvent('eventrender', this, ev);
59793         
59794         for(var i =0; i < rows.length; i++) {
59795             
59796             cls = '';
59797             if (i == 0) {
59798                 cls += ' fc-event-start';
59799             }
59800             if ((i+1) == rows.length) {
59801                 cls += ' fc-event-end';
59802             }
59803             
59804             //Roo.log(ev.data);
59805             // how many rows should it span..
59806             var cg = this.eventTmpl.append(ctr,Roo.apply({
59807                 fccls : cls
59808                 
59809             }, ev.data) , true);
59810             
59811             
59812             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59813             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59814             cg.on('click', this.onEventClick, this, ev);
59815             
59816             ev.els.push(cg);
59817             
59818             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59819             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59820             //Roo.log(cg);
59821              
59822             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59823             cg.setWidth(ebox.right - sbox.x -2);
59824         }
59825     },
59826     
59827     renderEvents: function()
59828     {   
59829         // first make sure there is enough space..
59830         
59831         if (!this.eventTmpl) {
59832             this.eventTmpl = new Roo.Template(
59833                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59834                     '<div class="fc-event-inner">' +
59835                         '<span class="fc-event-time">{time}</span>' +
59836                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59837                     '</div>' +
59838                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59839                 '</div>'
59840             );
59841                 
59842         }
59843                
59844         
59845         
59846         this.cells.each(function(c) {
59847             //Roo.log(c.select('.fc-day-content div',true).first());
59848             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59849         });
59850         
59851         var ctr = this.view.el.select('.fc-event-container',true).first();
59852         
59853         var cls;
59854         this.eventStore.each(function(ev){
59855             
59856             this.renderEvent(ev);
59857              
59858              
59859         }, this);
59860         this.view.layout();
59861         
59862     },
59863     
59864     onEventEnter: function (e, el,event,d) {
59865         this.fireEvent('evententer', this, el, event);
59866     },
59867     
59868     onEventLeave: function (e, el,event,d) {
59869         this.fireEvent('eventleave', this, el, event);
59870     },
59871     
59872     onEventClick: function (e, el,event,d) {
59873         this.fireEvent('eventclick', this, el, event);
59874     },
59875     
59876     onMonthChange: function () {
59877         this.store.load();
59878     },
59879     
59880     onLoad: function () {
59881         
59882         //Roo.log('calendar onload');
59883 //         
59884         if(this.eventStore.getCount() > 0){
59885             
59886            
59887             
59888             this.eventStore.each(function(d){
59889                 
59890                 
59891                 // FIXME..
59892                 var add =   d.data;
59893                 if (typeof(add.end_dt) == 'undefined')  {
59894                     Roo.log("Missing End time in calendar data: ");
59895                     Roo.log(d);
59896                     return;
59897                 }
59898                 if (typeof(add.start_dt) == 'undefined')  {
59899                     Roo.log("Missing Start time in calendar data: ");
59900                     Roo.log(d);
59901                     return;
59902                 }
59903                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59904                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59905                 add.id = add.id || d.id;
59906                 add.title = add.title || '??';
59907                 
59908                 this.addItem(d);
59909                 
59910              
59911             },this);
59912         }
59913         
59914         this.renderEvents();
59915     }
59916     
59917
59918 });
59919 /*
59920  grid : {
59921                 xtype: 'Grid',
59922                 xns: Roo.grid,
59923                 listeners : {
59924                     render : function ()
59925                     {
59926                         _this.grid = this;
59927                         
59928                         if (!this.view.el.hasClass('course-timesheet')) {
59929                             this.view.el.addClass('course-timesheet');
59930                         }
59931                         if (this.tsStyle) {
59932                             this.ds.load({});
59933                             return; 
59934                         }
59935                         Roo.log('width');
59936                         Roo.log(_this.grid.view.el.getWidth());
59937                         
59938                         
59939                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59940                             '.course-timesheet .x-grid-row' : {
59941                                 height: '80px'
59942                             },
59943                             '.x-grid-row td' : {
59944                                 'vertical-align' : 0
59945                             },
59946                             '.course-edit-link' : {
59947                                 'color' : 'blue',
59948                                 'text-overflow' : 'ellipsis',
59949                                 'overflow' : 'hidden',
59950                                 'white-space' : 'nowrap',
59951                                 'cursor' : 'pointer'
59952                             },
59953                             '.sub-link' : {
59954                                 'color' : 'green'
59955                             },
59956                             '.de-act-sup-link' : {
59957                                 'color' : 'purple',
59958                                 'text-decoration' : 'line-through'
59959                             },
59960                             '.de-act-link' : {
59961                                 'color' : 'red',
59962                                 'text-decoration' : 'line-through'
59963                             },
59964                             '.course-timesheet .course-highlight' : {
59965                                 'border-top-style': 'dashed !important',
59966                                 'border-bottom-bottom': 'dashed !important'
59967                             },
59968                             '.course-timesheet .course-item' : {
59969                                 'font-family'   : 'tahoma, arial, helvetica',
59970                                 'font-size'     : '11px',
59971                                 'overflow'      : 'hidden',
59972                                 'padding-left'  : '10px',
59973                                 'padding-right' : '10px',
59974                                 'padding-top' : '10px' 
59975                             }
59976                             
59977                         }, Roo.id());
59978                                 this.ds.load({});
59979                     }
59980                 },
59981                 autoWidth : true,
59982                 monitorWindowResize : false,
59983                 cellrenderer : function(v,x,r)
59984                 {
59985                     return v;
59986                 },
59987                 sm : {
59988                     xtype: 'CellSelectionModel',
59989                     xns: Roo.grid
59990                 },
59991                 dataSource : {
59992                     xtype: 'Store',
59993                     xns: Roo.data,
59994                     listeners : {
59995                         beforeload : function (_self, options)
59996                         {
59997                             options.params = options.params || {};
59998                             options.params._month = _this.monthField.getValue();
59999                             options.params.limit = 9999;
60000                             options.params['sort'] = 'when_dt';    
60001                             options.params['dir'] = 'ASC';    
60002                             this.proxy.loadResponse = this.loadResponse;
60003                             Roo.log("load?");
60004                             //this.addColumns();
60005                         },
60006                         load : function (_self, records, options)
60007                         {
60008                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60009                                 // if you click on the translation.. you can edit it...
60010                                 var el = Roo.get(this);
60011                                 var id = el.dom.getAttribute('data-id');
60012                                 var d = el.dom.getAttribute('data-date');
60013                                 var t = el.dom.getAttribute('data-time');
60014                                 //var id = this.child('span').dom.textContent;
60015                                 
60016                                 //Roo.log(this);
60017                                 Pman.Dialog.CourseCalendar.show({
60018                                     id : id,
60019                                     when_d : d,
60020                                     when_t : t,
60021                                     productitem_active : id ? 1 : 0
60022                                 }, function() {
60023                                     _this.grid.ds.load({});
60024                                 });
60025                            
60026                            });
60027                            
60028                            _this.panel.fireEvent('resize', [ '', '' ]);
60029                         }
60030                     },
60031                     loadResponse : function(o, success, response){
60032                             // this is overridden on before load..
60033                             
60034                             Roo.log("our code?");       
60035                             //Roo.log(success);
60036                             //Roo.log(response)
60037                             delete this.activeRequest;
60038                             if(!success){
60039                                 this.fireEvent("loadexception", this, o, response);
60040                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60041                                 return;
60042                             }
60043                             var result;
60044                             try {
60045                                 result = o.reader.read(response);
60046                             }catch(e){
60047                                 Roo.log("load exception?");
60048                                 this.fireEvent("loadexception", this, o, response, e);
60049                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60050                                 return;
60051                             }
60052                             Roo.log("ready...");        
60053                             // loop through result.records;
60054                             // and set this.tdate[date] = [] << array of records..
60055                             _this.tdata  = {};
60056                             Roo.each(result.records, function(r){
60057                                 //Roo.log(r.data);
60058                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60059                                     _this.tdata[r.data.when_dt.format('j')] = [];
60060                                 }
60061                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60062                             });
60063                             
60064                             //Roo.log(_this.tdata);
60065                             
60066                             result.records = [];
60067                             result.totalRecords = 6;
60068                     
60069                             // let's generate some duumy records for the rows.
60070                             //var st = _this.dateField.getValue();
60071                             
60072                             // work out monday..
60073                             //st = st.add(Date.DAY, -1 * st.format('w'));
60074                             
60075                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60076                             
60077                             var firstOfMonth = date.getFirstDayOfMonth();
60078                             var days = date.getDaysInMonth();
60079                             var d = 1;
60080                             var firstAdded = false;
60081                             for (var i = 0; i < result.totalRecords ; i++) {
60082                                 //var d= st.add(Date.DAY, i);
60083                                 var row = {};
60084                                 var added = 0;
60085                                 for(var w = 0 ; w < 7 ; w++){
60086                                     if(!firstAdded && firstOfMonth != w){
60087                                         continue;
60088                                     }
60089                                     if(d > days){
60090                                         continue;
60091                                     }
60092                                     firstAdded = true;
60093                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60094                                     row['weekday'+w] = String.format(
60095                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60096                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60097                                                     d,
60098                                                     date.format('Y-m-')+dd
60099                                                 );
60100                                     added++;
60101                                     if(typeof(_this.tdata[d]) != 'undefined'){
60102                                         Roo.each(_this.tdata[d], function(r){
60103                                             var is_sub = '';
60104                                             var deactive = '';
60105                                             var id = r.id;
60106                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60107                                             if(r.parent_id*1>0){
60108                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60109                                                 id = r.parent_id;
60110                                             }
60111                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60112                                                 deactive = 'de-act-link';
60113                                             }
60114                                             
60115                                             row['weekday'+w] += String.format(
60116                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60117                                                     id, //0
60118                                                     r.product_id_name, //1
60119                                                     r.when_dt.format('h:ia'), //2
60120                                                     is_sub, //3
60121                                                     deactive, //4
60122                                                     desc // 5
60123                                             );
60124                                         });
60125                                     }
60126                                     d++;
60127                                 }
60128                                 
60129                                 // only do this if something added..
60130                                 if(added > 0){ 
60131                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60132                                 }
60133                                 
60134                                 
60135                                 // push it twice. (second one with an hour..
60136                                 
60137                             }
60138                             //Roo.log(result);
60139                             this.fireEvent("load", this, o, o.request.arg);
60140                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60141                         },
60142                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60143                     proxy : {
60144                         xtype: 'HttpProxy',
60145                         xns: Roo.data,
60146                         method : 'GET',
60147                         url : baseURL + '/Roo/Shop_course.php'
60148                     },
60149                     reader : {
60150                         xtype: 'JsonReader',
60151                         xns: Roo.data,
60152                         id : 'id',
60153                         fields : [
60154                             {
60155                                 'name': 'id',
60156                                 'type': 'int'
60157                             },
60158                             {
60159                                 'name': 'when_dt',
60160                                 'type': 'string'
60161                             },
60162                             {
60163                                 'name': 'end_dt',
60164                                 'type': 'string'
60165                             },
60166                             {
60167                                 'name': 'parent_id',
60168                                 'type': 'int'
60169                             },
60170                             {
60171                                 'name': 'product_id',
60172                                 'type': 'int'
60173                             },
60174                             {
60175                                 'name': 'productitem_id',
60176                                 'type': 'int'
60177                             },
60178                             {
60179                                 'name': 'guid',
60180                                 'type': 'int'
60181                             }
60182                         ]
60183                     }
60184                 },
60185                 toolbar : {
60186                     xtype: 'Toolbar',
60187                     xns: Roo,
60188                     items : [
60189                         {
60190                             xtype: 'Button',
60191                             xns: Roo.Toolbar,
60192                             listeners : {
60193                                 click : function (_self, e)
60194                                 {
60195                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60196                                     sd.setMonth(sd.getMonth()-1);
60197                                     _this.monthField.setValue(sd.format('Y-m-d'));
60198                                     _this.grid.ds.load({});
60199                                 }
60200                             },
60201                             text : "Back"
60202                         },
60203                         {
60204                             xtype: 'Separator',
60205                             xns: Roo.Toolbar
60206                         },
60207                         {
60208                             xtype: 'MonthField',
60209                             xns: Roo.form,
60210                             listeners : {
60211                                 render : function (_self)
60212                                 {
60213                                     _this.monthField = _self;
60214                                    // _this.monthField.set  today
60215                                 },
60216                                 select : function (combo, date)
60217                                 {
60218                                     _this.grid.ds.load({});
60219                                 }
60220                             },
60221                             value : (function() { return new Date(); })()
60222                         },
60223                         {
60224                             xtype: 'Separator',
60225                             xns: Roo.Toolbar
60226                         },
60227                         {
60228                             xtype: 'TextItem',
60229                             xns: Roo.Toolbar,
60230                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60231                         },
60232                         {
60233                             xtype: 'Fill',
60234                             xns: Roo.Toolbar
60235                         },
60236                         {
60237                             xtype: 'Button',
60238                             xns: Roo.Toolbar,
60239                             listeners : {
60240                                 click : function (_self, e)
60241                                 {
60242                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60243                                     sd.setMonth(sd.getMonth()+1);
60244                                     _this.monthField.setValue(sd.format('Y-m-d'));
60245                                     _this.grid.ds.load({});
60246                                 }
60247                             },
60248                             text : "Next"
60249                         }
60250                     ]
60251                 },
60252                  
60253             }
60254         };
60255         
60256         *//*
60257  * Based on:
60258  * Ext JS Library 1.1.1
60259  * Copyright(c) 2006-2007, Ext JS, LLC.
60260  *
60261  * Originally Released Under LGPL - original licence link has changed is not relivant.
60262  *
60263  * Fork - LGPL
60264  * <script type="text/javascript">
60265  */
60266  
60267 /**
60268  * @class Roo.LoadMask
60269  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60270  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60271  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60272  * element's UpdateManager load indicator and will be destroyed after the initial load.
60273  * @constructor
60274  * Create a new LoadMask
60275  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60276  * @param {Object} config The config object
60277  */
60278 Roo.LoadMask = function(el, config){
60279     this.el = Roo.get(el);
60280     Roo.apply(this, config);
60281     if(this.store){
60282         this.store.on('beforeload', this.onBeforeLoad, this);
60283         this.store.on('load', this.onLoad, this);
60284         this.store.on('loadexception', this.onLoadException, this);
60285         this.removeMask = false;
60286     }else{
60287         var um = this.el.getUpdateManager();
60288         um.showLoadIndicator = false; // disable the default indicator
60289         um.on('beforeupdate', this.onBeforeLoad, this);
60290         um.on('update', this.onLoad, this);
60291         um.on('failure', this.onLoad, this);
60292         this.removeMask = true;
60293     }
60294 };
60295
60296 Roo.LoadMask.prototype = {
60297     /**
60298      * @cfg {Boolean} removeMask
60299      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60300      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60301      */
60302     /**
60303      * @cfg {String} msg
60304      * The text to display in a centered loading message box (defaults to 'Loading...')
60305      */
60306     msg : 'Loading...',
60307     /**
60308      * @cfg {String} msgCls
60309      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60310      */
60311     msgCls : 'x-mask-loading',
60312
60313     /**
60314      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60315      * @type Boolean
60316      */
60317     disabled: false,
60318
60319     /**
60320      * Disables the mask to prevent it from being displayed
60321      */
60322     disable : function(){
60323        this.disabled = true;
60324     },
60325
60326     /**
60327      * Enables the mask so that it can be displayed
60328      */
60329     enable : function(){
60330         this.disabled = false;
60331     },
60332     
60333     onLoadException : function()
60334     {
60335         Roo.log(arguments);
60336         
60337         if (typeof(arguments[3]) != 'undefined') {
60338             Roo.MessageBox.alert("Error loading",arguments[3]);
60339         } 
60340         /*
60341         try {
60342             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60343                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60344             }   
60345         } catch(e) {
60346             
60347         }
60348         */
60349     
60350         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60351     },
60352     // private
60353     onLoad : function()
60354     {
60355         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60356     },
60357
60358     // private
60359     onBeforeLoad : function(){
60360         if(!this.disabled){
60361             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60362         }
60363     },
60364
60365     // private
60366     destroy : function(){
60367         if(this.store){
60368             this.store.un('beforeload', this.onBeforeLoad, this);
60369             this.store.un('load', this.onLoad, this);
60370             this.store.un('loadexception', this.onLoadException, this);
60371         }else{
60372             var um = this.el.getUpdateManager();
60373             um.un('beforeupdate', this.onBeforeLoad, this);
60374             um.un('update', this.onLoad, this);
60375             um.un('failure', this.onLoad, this);
60376         }
60377     }
60378 };/*
60379  * Based on:
60380  * Ext JS Library 1.1.1
60381  * Copyright(c) 2006-2007, Ext JS, LLC.
60382  *
60383  * Originally Released Under LGPL - original licence link has changed is not relivant.
60384  *
60385  * Fork - LGPL
60386  * <script type="text/javascript">
60387  */
60388
60389
60390 /**
60391  * @class Roo.XTemplate
60392  * @extends Roo.Template
60393  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60394 <pre><code>
60395 var t = new Roo.XTemplate(
60396         '&lt;select name="{name}"&gt;',
60397                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60398         '&lt;/select&gt;'
60399 );
60400  
60401 // then append, applying the master template values
60402  </code></pre>
60403  *
60404  * Supported features:
60405  *
60406  *  Tags:
60407
60408 <pre><code>
60409       {a_variable} - output encoded.
60410       {a_variable.format:("Y-m-d")} - call a method on the variable
60411       {a_variable:raw} - unencoded output
60412       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60413       {a_variable:this.method_on_template(...)} - call a method on the template object.
60414  
60415 </code></pre>
60416  *  The tpl tag:
60417 <pre><code>
60418         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60419         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60420         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60421         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60422   
60423         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60424         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60425 </code></pre>
60426  *      
60427  */
60428 Roo.XTemplate = function()
60429 {
60430     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60431     if (this.html) {
60432         this.compile();
60433     }
60434 };
60435
60436
60437 Roo.extend(Roo.XTemplate, Roo.Template, {
60438
60439     /**
60440      * The various sub templates
60441      */
60442     tpls : false,
60443     /**
60444      *
60445      * basic tag replacing syntax
60446      * WORD:WORD()
60447      *
60448      * // you can fake an object call by doing this
60449      *  x.t:(test,tesT) 
60450      * 
60451      */
60452     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60453
60454     /**
60455      * compile the template
60456      *
60457      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60458      *
60459      */
60460     compile: function()
60461     {
60462         var s = this.html;
60463      
60464         s = ['<tpl>', s, '</tpl>'].join('');
60465     
60466         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60467             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60468             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60469             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60470             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60471             m,
60472             id     = 0,
60473             tpls   = [];
60474     
60475         while(true == !!(m = s.match(re))){
60476             var forMatch   = m[0].match(nameRe),
60477                 ifMatch   = m[0].match(ifRe),
60478                 execMatch   = m[0].match(execRe),
60479                 namedMatch   = m[0].match(namedRe),
60480                 
60481                 exp  = null, 
60482                 fn   = null,
60483                 exec = null,
60484                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60485                 
60486             if (ifMatch) {
60487                 // if - puts fn into test..
60488                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60489                 if(exp){
60490                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60491                 }
60492             }
60493             
60494             if (execMatch) {
60495                 // exec - calls a function... returns empty if true is  returned.
60496                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60497                 if(exp){
60498                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60499                 }
60500             }
60501             
60502             
60503             if (name) {
60504                 // for = 
60505                 switch(name){
60506                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60507                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60508                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60509                 }
60510             }
60511             var uid = namedMatch ? namedMatch[1] : id;
60512             
60513             
60514             tpls.push({
60515                 id:     namedMatch ? namedMatch[1] : id,
60516                 target: name,
60517                 exec:   exec,
60518                 test:   fn,
60519                 body:   m[1] || ''
60520             });
60521             if (namedMatch) {
60522                 s = s.replace(m[0], '');
60523             } else { 
60524                 s = s.replace(m[0], '{xtpl'+ id + '}');
60525             }
60526             ++id;
60527         }
60528         this.tpls = [];
60529         for(var i = tpls.length-1; i >= 0; --i){
60530             this.compileTpl(tpls[i]);
60531             this.tpls[tpls[i].id] = tpls[i];
60532         }
60533         this.master = tpls[tpls.length-1];
60534         return this;
60535     },
60536     /**
60537      * same as applyTemplate, except it's done to one of the subTemplates
60538      * when using named templates, you can do:
60539      *
60540      * var str = pl.applySubTemplate('your-name', values);
60541      *
60542      * 
60543      * @param {Number} id of the template
60544      * @param {Object} values to apply to template
60545      * @param {Object} parent (normaly the instance of this object)
60546      */
60547     applySubTemplate : function(id, values, parent)
60548     {
60549         
60550         
60551         var t = this.tpls[id];
60552         
60553         
60554         try { 
60555             if(t.test && !t.test.call(this, values, parent)){
60556                 return '';
60557             }
60558         } catch(e) {
60559             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60560             Roo.log(e.toString());
60561             Roo.log(t.test);
60562             return ''
60563         }
60564         try { 
60565             
60566             if(t.exec && t.exec.call(this, values, parent)){
60567                 return '';
60568             }
60569         } catch(e) {
60570             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60571             Roo.log(e.toString());
60572             Roo.log(t.exec);
60573             return ''
60574         }
60575         try {
60576             var vs = t.target ? t.target.call(this, values, parent) : values;
60577             parent = t.target ? values : parent;
60578             if(t.target && vs instanceof Array){
60579                 var buf = [];
60580                 for(var i = 0, len = vs.length; i < len; i++){
60581                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60582                 }
60583                 return buf.join('');
60584             }
60585             return t.compiled.call(this, vs, parent);
60586         } catch (e) {
60587             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60588             Roo.log(e.toString());
60589             Roo.log(t.compiled);
60590             return '';
60591         }
60592     },
60593
60594     compileTpl : function(tpl)
60595     {
60596         var fm = Roo.util.Format;
60597         var useF = this.disableFormats !== true;
60598         var sep = Roo.isGecko ? "+" : ",";
60599         var undef = function(str) {
60600             Roo.log("Property not found :"  + str);
60601             return '';
60602         };
60603         
60604         var fn = function(m, name, format, args)
60605         {
60606             //Roo.log(arguments);
60607             args = args ? args.replace(/\\'/g,"'") : args;
60608             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60609             if (typeof(format) == 'undefined') {
60610                 format= 'htmlEncode';
60611             }
60612             if (format == 'raw' ) {
60613                 format = false;
60614             }
60615             
60616             if(name.substr(0, 4) == 'xtpl'){
60617                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60618             }
60619             
60620             // build an array of options to determine if value is undefined..
60621             
60622             // basically get 'xxxx.yyyy' then do
60623             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60624             //    (function () { Roo.log("Property not found"); return ''; })() :
60625             //    ......
60626             
60627             var udef_ar = [];
60628             var lookfor = '';
60629             Roo.each(name.split('.'), function(st) {
60630                 lookfor += (lookfor.length ? '.': '') + st;
60631                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60632             });
60633             
60634             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60635             
60636             
60637             if(format && useF){
60638                 
60639                 args = args ? ',' + args : "";
60640                  
60641                 if(format.substr(0, 5) != "this."){
60642                     format = "fm." + format + '(';
60643                 }else{
60644                     format = 'this.call("'+ format.substr(5) + '", ';
60645                     args = ", values";
60646                 }
60647                 
60648                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60649             }
60650              
60651             if (args.length) {
60652                 // called with xxyx.yuu:(test,test)
60653                 // change to ()
60654                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60655             }
60656             // raw.. - :raw modifier..
60657             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60658             
60659         };
60660         var body;
60661         // branched to use + in gecko and [].join() in others
60662         if(Roo.isGecko){
60663             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60664                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60665                     "';};};";
60666         }else{
60667             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60668             body.push(tpl.body.replace(/(\r\n|\n)/g,
60669                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60670             body.push("'].join('');};};");
60671             body = body.join('');
60672         }
60673         
60674         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60675        
60676         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60677         eval(body);
60678         
60679         return this;
60680     },
60681
60682     applyTemplate : function(values){
60683         return this.master.compiled.call(this, values, {});
60684         //var s = this.subs;
60685     },
60686
60687     apply : function(){
60688         return this.applyTemplate.apply(this, arguments);
60689     }
60690
60691  });
60692
60693 Roo.XTemplate.from = function(el){
60694     el = Roo.getDom(el);
60695     return new Roo.XTemplate(el.value || el.innerHTML);
60696 };