roojs-core.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         Roo.log(["applyTemplate", values]);
4671         try {
4672            
4673             if(this.compiled){
4674                 return this.compiled(values);
4675             }
4676             var useF = this.disableFormats !== true;
4677             var fm = Roo.util.Format, tpl = this;
4678             var fn = function(m, name, format, args){
4679                 if(format && useF){
4680                     if(format.substr(0, 5) == "this."){
4681                         return tpl.call(format.substr(5), values[name], values);
4682                     }else{
4683                         if(args){
4684                             // quoted values are required for strings in compiled templates, 
4685                             // but for non compiled we need to strip them
4686                             // quoted reversed for jsmin
4687                             var re = /^\s*['"](.*)["']\s*$/;
4688                             args = args.split(',');
4689                             for(var i = 0, len = args.length; i < len; i++){
4690                                 args[i] = args[i].replace(re, "$1");
4691                             }
4692                             args = [values[name]].concat(args);
4693                         }else{
4694                             args = [values[name]];
4695                         }
4696                         return fm[format].apply(fm, args);
4697                     }
4698                 }else{
4699                     return values[name] !== undefined ? values[name] : "";
4700                 }
4701             };
4702             return this.html.replace(this.re, fn);
4703         } catch (e) {
4704             Roo.log(e);
4705             throw e;
4706         }
4707          
4708     },
4709     
4710     loading : false,
4711       
4712     load : function ()
4713     {
4714          
4715         if (this.loading) {
4716             return;
4717         }
4718         var _t = this;
4719         
4720         this.loading = true;
4721         this.compiled = false;
4722         
4723         var cx = new Roo.data.Connection();
4724         cx.request({
4725             url : this.url,
4726             method : 'GET',
4727             success : function (response) {
4728                 _t.loading = false;
4729                 _t.html = response.responseText;
4730                 _t.url = false;
4731                 _t.compile();
4732              },
4733             failure : function(response) {
4734                 Roo.log("Template failed to load from " + _t.url);
4735                 _t.loading = false;
4736             }
4737         });
4738     },
4739
4740     /**
4741      * Sets the HTML used as the template and optionally compiles it.
4742      * @param {String} html
4743      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4744      * @return {Roo.Template} this
4745      */
4746     set : function(html, compile){
4747         this.html = html;
4748         this.compiled = null;
4749         if(compile){
4750             this.compile();
4751         }
4752         return this;
4753     },
4754     
4755     /**
4756      * True to disable format functions (defaults to false)
4757      * @type Boolean
4758      */
4759     disableFormats : false,
4760     
4761     /**
4762     * The regular expression used to match template variables 
4763     * @type RegExp
4764     * @property 
4765     */
4766     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4767     
4768     /**
4769      * Compiles the template into an internal function, eliminating the RegEx overhead.
4770      * @return {Roo.Template} this
4771      */
4772     compile : function(){
4773         var fm = Roo.util.Format;
4774         var useF = this.disableFormats !== true;
4775         var sep = Roo.isGecko ? "+" : ",";
4776         var fn = function(m, name, format, args){
4777             if(format && useF){
4778                 args = args ? ',' + args : "";
4779                 if(format.substr(0, 5) != "this."){
4780                     format = "fm." + format + '(';
4781                 }else{
4782                     format = 'this.call("'+ format.substr(5) + '", ';
4783                     args = ", values";
4784                 }
4785             }else{
4786                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4787             }
4788             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4789         };
4790         var body;
4791         // branched to use + in gecko and [].join() in others
4792         if(Roo.isGecko){
4793             body = "this.compiled = function(values){ return '" +
4794                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4795                     "';};";
4796         }else{
4797             body = ["this.compiled = function(values){ return ['"];
4798             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4799             body.push("'].join('');};");
4800             body = body.join('');
4801         }
4802         /**
4803          * eval:var:values
4804          * eval:var:fm
4805          */
4806         eval(body);
4807         return this;
4808     },
4809     
4810     // private function used to call members
4811     call : function(fnName, value, allValues){
4812         return this[fnName](value, allValues);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     insertFirst: function(el, values, returnElement){
4823         return this.doInsert('afterBegin', el, values, returnElement);
4824     },
4825
4826     /**
4827      * Applies the supplied values to the template and inserts the new node(s) before el.
4828      * @param {String/HTMLElement/Roo.Element} el The context element
4829      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4830      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4831      * @return {HTMLElement/Roo.Element} The new node or Element
4832      */
4833     insertBefore: function(el, values, returnElement){
4834         return this.doInsert('beforeBegin', el, values, returnElement);
4835     },
4836
4837     /**
4838      * Applies the supplied values to the template and inserts the new node(s) after el.
4839      * @param {String/HTMLElement/Roo.Element} el The context element
4840      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4841      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4842      * @return {HTMLElement/Roo.Element} The new node or Element
4843      */
4844     insertAfter : function(el, values, returnElement){
4845         return this.doInsert('afterEnd', el, values, returnElement);
4846     },
4847     
4848     /**
4849      * Applies the supplied values to the template and appends the new node(s) to el.
4850      * @param {String/HTMLElement/Roo.Element} el The context element
4851      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4852      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4853      * @return {HTMLElement/Roo.Element} The new node or Element
4854      */
4855     append : function(el, values, returnElement){
4856         return this.doInsert('beforeEnd', el, values, returnElement);
4857     },
4858
4859     doInsert : function(where, el, values, returnEl){
4860         el = Roo.getDom(el);
4861         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4862         return returnEl ? Roo.get(newNode, true) : newNode;
4863     },
4864
4865     /**
4866      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4867      * @param {String/HTMLElement/Roo.Element} el The context element
4868      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4869      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4870      * @return {HTMLElement/Roo.Element} The new node or Element
4871      */
4872     overwrite : function(el, values, returnElement){
4873         el = Roo.getDom(el);
4874         el.innerHTML = this.applyTemplate(values);
4875         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4876     }
4877 };
4878 /**
4879  * Alias for {@link #applyTemplate}
4880  * @method
4881  */
4882 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4883
4884 // backwards compat
4885 Roo.DomHelper.Template = Roo.Template;
4886
4887 /**
4888  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4889  * @param {String/HTMLElement} el A DOM element or its id
4890  * @returns {Roo.Template} The created template
4891  * @static
4892  */
4893 Roo.Template.from = function(el){
4894     el = Roo.getDom(el);
4895     return new Roo.Template(el.value || el.innerHTML);
4896 };/*
4897  * Based on:
4898  * Ext JS Library 1.1.1
4899  * Copyright(c) 2006-2007, Ext JS, LLC.
4900  *
4901  * Originally Released Under LGPL - original licence link has changed is not relivant.
4902  *
4903  * Fork - LGPL
4904  * <script type="text/javascript">
4905  */
4906  
4907
4908 /*
4909  * This is code is also distributed under MIT license for use
4910  * with jQuery and prototype JavaScript libraries.
4911  */
4912 /**
4913  * @class Roo.DomQuery
4914 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4915 <p>
4916 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4917
4918 <p>
4919 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4920 </p>
4921 <h4>Element Selectors:</h4>
4922 <ul class="list">
4923     <li> <b>*</b> any element</li>
4924     <li> <b>E</b> an element with the tag E</li>
4925     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4926     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4927     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4928     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4929 </ul>
4930 <h4>Attribute Selectors:</h4>
4931 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4932 <ul class="list">
4933     <li> <b>E[foo]</b> has an attribute "foo"</li>
4934     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4935     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4936     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4937     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4938     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4939     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4940 </ul>
4941 <h4>Pseudo Classes:</h4>
4942 <ul class="list">
4943     <li> <b>E:first-child</b> E is the first child of its parent</li>
4944     <li> <b>E:last-child</b> E is the last child of its parent</li>
4945     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4946     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4947     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4948     <li> <b>E:only-child</b> E is the only child of its parent</li>
4949     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4950     <li> <b>E:first</b> the first E in the resultset</li>
4951     <li> <b>E:last</b> the last E in the resultset</li>
4952     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4953     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4954     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4955     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4956     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4957     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4958     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4959     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4960     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4961 </ul>
4962 <h4>CSS Value Selectors:</h4>
4963 <ul class="list">
4964     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4965     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4966     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4967     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4968     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4969     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4970 </ul>
4971  * @singleton
4972  */
4973 Roo.DomQuery = function(){
4974     var cache = {}, simpleCache = {}, valueCache = {};
4975     var nonSpace = /\S/;
4976     var trimRe = /^\s+|\s+$/g;
4977     var tplRe = /\{(\d+)\}/g;
4978     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4979     var tagTokenRe = /^(#)?([\w-\*]+)/;
4980     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4981
4982     function child(p, index){
4983         var i = 0;
4984         var n = p.firstChild;
4985         while(n){
4986             if(n.nodeType == 1){
4987                if(++i == index){
4988                    return n;
4989                }
4990             }
4991             n = n.nextSibling;
4992         }
4993         return null;
4994     };
4995
4996     function next(n){
4997         while((n = n.nextSibling) && n.nodeType != 1);
4998         return n;
4999     };
5000
5001     function prev(n){
5002         while((n = n.previousSibling) && n.nodeType != 1);
5003         return n;
5004     };
5005
5006     function children(d){
5007         var n = d.firstChild, ni = -1;
5008             while(n){
5009                 var nx = n.nextSibling;
5010                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5011                     d.removeChild(n);
5012                 }else{
5013                     n.nodeIndex = ++ni;
5014                 }
5015                 n = nx;
5016             }
5017             return this;
5018         };
5019
5020     function byClassName(c, a, v){
5021         if(!v){
5022             return c;
5023         }
5024         var r = [], ri = -1, cn;
5025         for(var i = 0, ci; ci = c[i]; i++){
5026             if((' '+ci.className+' ').indexOf(v) != -1){
5027                 r[++ri] = ci;
5028             }
5029         }
5030         return r;
5031     };
5032
5033     function attrValue(n, attr){
5034         if(!n.tagName && typeof n.length != "undefined"){
5035             n = n[0];
5036         }
5037         if(!n){
5038             return null;
5039         }
5040         if(attr == "for"){
5041             return n.htmlFor;
5042         }
5043         if(attr == "class" || attr == "className"){
5044             return n.className;
5045         }
5046         return n.getAttribute(attr) || n[attr];
5047
5048     };
5049
5050     function getNodes(ns, mode, tagName){
5051         var result = [], ri = -1, cs;
5052         if(!ns){
5053             return result;
5054         }
5055         tagName = tagName || "*";
5056         if(typeof ns.getElementsByTagName != "undefined"){
5057             ns = [ns];
5058         }
5059         if(!mode){
5060             for(var i = 0, ni; ni = ns[i]; i++){
5061                 cs = ni.getElementsByTagName(tagName);
5062                 for(var j = 0, ci; ci = cs[j]; j++){
5063                     result[++ri] = ci;
5064                 }
5065             }
5066         }else if(mode == "/" || mode == ">"){
5067             var utag = tagName.toUpperCase();
5068             for(var i = 0, ni, cn; ni = ns[i]; i++){
5069                 cn = ni.children || ni.childNodes;
5070                 for(var j = 0, cj; cj = cn[j]; j++){
5071                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5072                         result[++ri] = cj;
5073                     }
5074                 }
5075             }
5076         }else if(mode == "+"){
5077             var utag = tagName.toUpperCase();
5078             for(var i = 0, n; n = ns[i]; i++){
5079                 while((n = n.nextSibling) && n.nodeType != 1);
5080                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5081                     result[++ri] = n;
5082                 }
5083             }
5084         }else if(mode == "~"){
5085             for(var i = 0, n; n = ns[i]; i++){
5086                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5087                 if(n){
5088                     result[++ri] = n;
5089                 }
5090             }
5091         }
5092         return result;
5093     };
5094
5095     function concat(a, b){
5096         if(b.slice){
5097             return a.concat(b);
5098         }
5099         for(var i = 0, l = b.length; i < l; i++){
5100             a[a.length] = b[i];
5101         }
5102         return a;
5103     }
5104
5105     function byTag(cs, tagName){
5106         if(cs.tagName || cs == document){
5107             cs = [cs];
5108         }
5109         if(!tagName){
5110             return cs;
5111         }
5112         var r = [], ri = -1;
5113         tagName = tagName.toLowerCase();
5114         for(var i = 0, ci; ci = cs[i]; i++){
5115             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5116                 r[++ri] = ci;
5117             }
5118         }
5119         return r;
5120     };
5121
5122     function byId(cs, attr, id){
5123         if(cs.tagName || cs == document){
5124             cs = [cs];
5125         }
5126         if(!id){
5127             return cs;
5128         }
5129         var r = [], ri = -1;
5130         for(var i = 0,ci; ci = cs[i]; i++){
5131             if(ci && ci.id == id){
5132                 r[++ri] = ci;
5133                 return r;
5134             }
5135         }
5136         return r;
5137     };
5138
5139     function byAttribute(cs, attr, value, op, custom){
5140         var r = [], ri = -1, st = custom=="{";
5141         var f = Roo.DomQuery.operators[op];
5142         for(var i = 0, ci; ci = cs[i]; i++){
5143             var a;
5144             if(st){
5145                 a = Roo.DomQuery.getStyle(ci, attr);
5146             }
5147             else if(attr == "class" || attr == "className"){
5148                 a = ci.className;
5149             }else if(attr == "for"){
5150                 a = ci.htmlFor;
5151             }else if(attr == "href"){
5152                 a = ci.getAttribute("href", 2);
5153             }else{
5154                 a = ci.getAttribute(attr);
5155             }
5156             if((f && f(a, value)) || (!f && a)){
5157                 r[++ri] = ci;
5158             }
5159         }
5160         return r;
5161     };
5162
5163     function byPseudo(cs, name, value){
5164         return Roo.DomQuery.pseudos[name](cs, value);
5165     };
5166
5167     // This is for IE MSXML which does not support expandos.
5168     // IE runs the same speed using setAttribute, however FF slows way down
5169     // and Safari completely fails so they need to continue to use expandos.
5170     var isIE = window.ActiveXObject ? true : false;
5171
5172     // this eval is stop the compressor from
5173     // renaming the variable to something shorter
5174     
5175     /** eval:var:batch */
5176     var batch = 30803; 
5177
5178     var key = 30803;
5179
5180     function nodupIEXml(cs){
5181         var d = ++key;
5182         cs[0].setAttribute("_nodup", d);
5183         var r = [cs[0]];
5184         for(var i = 1, len = cs.length; i < len; i++){
5185             var c = cs[i];
5186             if(!c.getAttribute("_nodup") != d){
5187                 c.setAttribute("_nodup", d);
5188                 r[r.length] = c;
5189             }
5190         }
5191         for(var i = 0, len = cs.length; i < len; i++){
5192             cs[i].removeAttribute("_nodup");
5193         }
5194         return r;
5195     }
5196
5197     function nodup(cs){
5198         if(!cs){
5199             return [];
5200         }
5201         var len = cs.length, c, i, r = cs, cj, ri = -1;
5202         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5203             return cs;
5204         }
5205         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5206             return nodupIEXml(cs);
5207         }
5208         var d = ++key;
5209         cs[0]._nodup = d;
5210         for(i = 1; c = cs[i]; i++){
5211             if(c._nodup != d){
5212                 c._nodup = d;
5213             }else{
5214                 r = [];
5215                 for(var j = 0; j < i; j++){
5216                     r[++ri] = cs[j];
5217                 }
5218                 for(j = i+1; cj = cs[j]; j++){
5219                     if(cj._nodup != d){
5220                         cj._nodup = d;
5221                         r[++ri] = cj;
5222                     }
5223                 }
5224                 return r;
5225             }
5226         }
5227         return r;
5228     }
5229
5230     function quickDiffIEXml(c1, c2){
5231         var d = ++key;
5232         for(var i = 0, len = c1.length; i < len; i++){
5233             c1[i].setAttribute("_qdiff", d);
5234         }
5235         var r = [];
5236         for(var i = 0, len = c2.length; i < len; i++){
5237             if(c2[i].getAttribute("_qdiff") != d){
5238                 r[r.length] = c2[i];
5239             }
5240         }
5241         for(var i = 0, len = c1.length; i < len; i++){
5242            c1[i].removeAttribute("_qdiff");
5243         }
5244         return r;
5245     }
5246
5247     function quickDiff(c1, c2){
5248         var len1 = c1.length;
5249         if(!len1){
5250             return c2;
5251         }
5252         if(isIE && c1[0].selectSingleNode){
5253             return quickDiffIEXml(c1, c2);
5254         }
5255         var d = ++key;
5256         for(var i = 0; i < len1; i++){
5257             c1[i]._qdiff = d;
5258         }
5259         var r = [];
5260         for(var i = 0, len = c2.length; i < len; i++){
5261             if(c2[i]._qdiff != d){
5262                 r[r.length] = c2[i];
5263             }
5264         }
5265         return r;
5266     }
5267
5268     function quickId(ns, mode, root, id){
5269         if(ns == root){
5270            var d = root.ownerDocument || root;
5271            return d.getElementById(id);
5272         }
5273         ns = getNodes(ns, mode, "*");
5274         return byId(ns, null, id);
5275     }
5276
5277     return {
5278         getStyle : function(el, name){
5279             return Roo.fly(el).getStyle(name);
5280         },
5281         /**
5282          * Compiles a selector/xpath query into a reusable function. The returned function
5283          * takes one parameter "root" (optional), which is the context node from where the query should start.
5284          * @param {String} selector The selector/xpath query
5285          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5286          * @return {Function}
5287          */
5288         compile : function(path, type){
5289             type = type || "select";
5290             
5291             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5292             var q = path, mode, lq;
5293             var tk = Roo.DomQuery.matchers;
5294             var tklen = tk.length;
5295             var mm;
5296
5297             // accept leading mode switch
5298             var lmode = q.match(modeRe);
5299             if(lmode && lmode[1]){
5300                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5301                 q = q.replace(lmode[1], "");
5302             }
5303             // strip leading slashes
5304             while(path.substr(0, 1)=="/"){
5305                 path = path.substr(1);
5306             }
5307
5308             while(q && lq != q){
5309                 lq = q;
5310                 var tm = q.match(tagTokenRe);
5311                 if(type == "select"){
5312                     if(tm){
5313                         if(tm[1] == "#"){
5314                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5315                         }else{
5316                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5317                         }
5318                         q = q.replace(tm[0], "");
5319                     }else if(q.substr(0, 1) != '@'){
5320                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5321                     }
5322                 }else{
5323                     if(tm){
5324                         if(tm[1] == "#"){
5325                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5326                         }else{
5327                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5328                         }
5329                         q = q.replace(tm[0], "");
5330                     }
5331                 }
5332                 while(!(mm = q.match(modeRe))){
5333                     var matched = false;
5334                     for(var j = 0; j < tklen; j++){
5335                         var t = tk[j];
5336                         var m = q.match(t.re);
5337                         if(m){
5338                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5339                                                     return m[i];
5340                                                 });
5341                             q = q.replace(m[0], "");
5342                             matched = true;
5343                             break;
5344                         }
5345                     }
5346                     // prevent infinite loop on bad selector
5347                     if(!matched){
5348                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5349                     }
5350                 }
5351                 if(mm[1]){
5352                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5353                     q = q.replace(mm[1], "");
5354                 }
5355             }
5356             fn[fn.length] = "return nodup(n);\n}";
5357             
5358              /** 
5359               * list of variables that need from compression as they are used by eval.
5360              *  eval:var:batch 
5361              *  eval:var:nodup
5362              *  eval:var:byTag
5363              *  eval:var:ById
5364              *  eval:var:getNodes
5365              *  eval:var:quickId
5366              *  eval:var:mode
5367              *  eval:var:root
5368              *  eval:var:n
5369              *  eval:var:byClassName
5370              *  eval:var:byPseudo
5371              *  eval:var:byAttribute
5372              *  eval:var:attrValue
5373              * 
5374              **/ 
5375             eval(fn.join(""));
5376             return f;
5377         },
5378
5379         /**
5380          * Selects a group of elements.
5381          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5382          * @param {Node} root (optional) The start of the query (defaults to document).
5383          * @return {Array}
5384          */
5385         select : function(path, root, type){
5386             if(!root || root == document){
5387                 root = document;
5388             }
5389             if(typeof root == "string"){
5390                 root = document.getElementById(root);
5391             }
5392             var paths = path.split(",");
5393             var results = [];
5394             for(var i = 0, len = paths.length; i < len; i++){
5395                 var p = paths[i].replace(trimRe, "");
5396                 if(!cache[p]){
5397                     cache[p] = Roo.DomQuery.compile(p);
5398                     if(!cache[p]){
5399                         throw p + " is not a valid selector";
5400                     }
5401                 }
5402                 var result = cache[p](root);
5403                 if(result && result != document){
5404                     results = results.concat(result);
5405                 }
5406             }
5407             if(paths.length > 1){
5408                 return nodup(results);
5409             }
5410             return results;
5411         },
5412
5413         /**
5414          * Selects a single element.
5415          * @param {String} selector The selector/xpath query
5416          * @param {Node} root (optional) The start of the query (defaults to document).
5417          * @return {Element}
5418          */
5419         selectNode : function(path, root){
5420             return Roo.DomQuery.select(path, root)[0];
5421         },
5422
5423         /**
5424          * Selects the value of a node, optionally replacing null with the defaultValue.
5425          * @param {String} selector The selector/xpath query
5426          * @param {Node} root (optional) The start of the query (defaults to document).
5427          * @param {String} defaultValue
5428          */
5429         selectValue : function(path, root, defaultValue){
5430             path = path.replace(trimRe, "");
5431             if(!valueCache[path]){
5432                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5433             }
5434             var n = valueCache[path](root);
5435             n = n[0] ? n[0] : n;
5436             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5437             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5438         },
5439
5440         /**
5441          * Selects the value of a node, parsing integers and floats.
5442          * @param {String} selector The selector/xpath query
5443          * @param {Node} root (optional) The start of the query (defaults to document).
5444          * @param {Number} defaultValue
5445          * @return {Number}
5446          */
5447         selectNumber : function(path, root, defaultValue){
5448             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5449             return parseFloat(v);
5450         },
5451
5452         /**
5453          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5454          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5455          * @param {String} selector The simple selector to test
5456          * @return {Boolean}
5457          */
5458         is : function(el, ss){
5459             if(typeof el == "string"){
5460                 el = document.getElementById(el);
5461             }
5462             var isArray = (el instanceof Array);
5463             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5464             return isArray ? (result.length == el.length) : (result.length > 0);
5465         },
5466
5467         /**
5468          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5469          * @param {Array} el An array of elements to filter
5470          * @param {String} selector The simple selector to test
5471          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5472          * the selector instead of the ones that match
5473          * @return {Array}
5474          */
5475         filter : function(els, ss, nonMatches){
5476             ss = ss.replace(trimRe, "");
5477             if(!simpleCache[ss]){
5478                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5479             }
5480             var result = simpleCache[ss](els);
5481             return nonMatches ? quickDiff(result, els) : result;
5482         },
5483
5484         /**
5485          * Collection of matching regular expressions and code snippets.
5486          */
5487         matchers : [{
5488                 re: /^\.([\w-]+)/,
5489                 select: 'n = byClassName(n, null, " {1} ");'
5490             }, {
5491                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5492                 select: 'n = byPseudo(n, "{1}", "{2}");'
5493             },{
5494                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5495                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5496             }, {
5497                 re: /^#([\w-]+)/,
5498                 select: 'n = byId(n, null, "{1}");'
5499             },{
5500                 re: /^@([\w-]+)/,
5501                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5502             }
5503         ],
5504
5505         /**
5506          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5507          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5508          */
5509         operators : {
5510             "=" : function(a, v){
5511                 return a == v;
5512             },
5513             "!=" : function(a, v){
5514                 return a != v;
5515             },
5516             "^=" : function(a, v){
5517                 return a && a.substr(0, v.length) == v;
5518             },
5519             "$=" : function(a, v){
5520                 return a && a.substr(a.length-v.length) == v;
5521             },
5522             "*=" : function(a, v){
5523                 return a && a.indexOf(v) !== -1;
5524             },
5525             "%=" : function(a, v){
5526                 return (a % v) == 0;
5527             },
5528             "|=" : function(a, v){
5529                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5530             },
5531             "~=" : function(a, v){
5532                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5533             }
5534         },
5535
5536         /**
5537          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5538          * and the argument (if any) supplied in the selector.
5539          */
5540         pseudos : {
5541             "first-child" : function(c){
5542                 var r = [], ri = -1, n;
5543                 for(var i = 0, ci; ci = n = c[i]; i++){
5544                     while((n = n.previousSibling) && n.nodeType != 1);
5545                     if(!n){
5546                         r[++ri] = ci;
5547                     }
5548                 }
5549                 return r;
5550             },
5551
5552             "last-child" : function(c){
5553                 var r = [], ri = -1, n;
5554                 for(var i = 0, ci; ci = n = c[i]; i++){
5555                     while((n = n.nextSibling) && n.nodeType != 1);
5556                     if(!n){
5557                         r[++ri] = ci;
5558                     }
5559                 }
5560                 return r;
5561             },
5562
5563             "nth-child" : function(c, a) {
5564                 var r = [], ri = -1;
5565                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5566                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5567                 for(var i = 0, n; n = c[i]; i++){
5568                     var pn = n.parentNode;
5569                     if (batch != pn._batch) {
5570                         var j = 0;
5571                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5572                             if(cn.nodeType == 1){
5573                                cn.nodeIndex = ++j;
5574                             }
5575                         }
5576                         pn._batch = batch;
5577                     }
5578                     if (f == 1) {
5579                         if (l == 0 || n.nodeIndex == l){
5580                             r[++ri] = n;
5581                         }
5582                     } else if ((n.nodeIndex + l) % f == 0){
5583                         r[++ri] = n;
5584                     }
5585                 }
5586
5587                 return r;
5588             },
5589
5590             "only-child" : function(c){
5591                 var r = [], ri = -1;;
5592                 for(var i = 0, ci; ci = c[i]; i++){
5593                     if(!prev(ci) && !next(ci)){
5594                         r[++ri] = ci;
5595                     }
5596                 }
5597                 return r;
5598             },
5599
5600             "empty" : function(c){
5601                 var r = [], ri = -1;
5602                 for(var i = 0, ci; ci = c[i]; i++){
5603                     var cns = ci.childNodes, j = 0, cn, empty = true;
5604                     while(cn = cns[j]){
5605                         ++j;
5606                         if(cn.nodeType == 1 || cn.nodeType == 3){
5607                             empty = false;
5608                             break;
5609                         }
5610                     }
5611                     if(empty){
5612                         r[++ri] = ci;
5613                     }
5614                 }
5615                 return r;
5616             },
5617
5618             "contains" : function(c, v){
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "nodeValue" : function(c, v){
5629                 var r = [], ri = -1;
5630                 for(var i = 0, ci; ci = c[i]; i++){
5631                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5632                         r[++ri] = ci;
5633                     }
5634                 }
5635                 return r;
5636             },
5637
5638             "checked" : function(c){
5639                 var r = [], ri = -1;
5640                 for(var i = 0, ci; ci = c[i]; i++){
5641                     if(ci.checked == true){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             },
5647
5648             "not" : function(c, ss){
5649                 return Roo.DomQuery.filter(c, ss, true);
5650             },
5651
5652             "odd" : function(c){
5653                 return this["nth-child"](c, "odd");
5654             },
5655
5656             "even" : function(c){
5657                 return this["nth-child"](c, "even");
5658             },
5659
5660             "nth" : function(c, a){
5661                 return c[a-1] || [];
5662             },
5663
5664             "first" : function(c){
5665                 return c[0] || [];
5666             },
5667
5668             "last" : function(c){
5669                 return c[c.length-1] || [];
5670             },
5671
5672             "has" : function(c, ss){
5673                 var s = Roo.DomQuery.select;
5674                 var r = [], ri = -1;
5675                 for(var i = 0, ci; ci = c[i]; i++){
5676                     if(s(ss, ci).length > 0){
5677                         r[++ri] = ci;
5678                     }
5679                 }
5680                 return r;
5681             },
5682
5683             "next" : function(c, ss){
5684                 var is = Roo.DomQuery.is;
5685                 var r = [], ri = -1;
5686                 for(var i = 0, ci; ci = c[i]; i++){
5687                     var n = next(ci);
5688                     if(n && is(n, ss)){
5689                         r[++ri] = ci;
5690                     }
5691                 }
5692                 return r;
5693             },
5694
5695             "prev" : function(c, ss){
5696                 var is = Roo.DomQuery.is;
5697                 var r = [], ri = -1;
5698                 for(var i = 0, ci; ci = c[i]; i++){
5699                     var n = prev(ci);
5700                     if(n && is(n, ss)){
5701                         r[++ri] = ci;
5702                     }
5703                 }
5704                 return r;
5705             }
5706         }
5707     };
5708 }();
5709
5710 /**
5711  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5712  * @param {String} path The selector/xpath query
5713  * @param {Node} root (optional) The start of the query (defaults to document).
5714  * @return {Array}
5715  * @member Roo
5716  * @method query
5717  */
5718 Roo.query = Roo.DomQuery.select;
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729
5730 /**
5731  * @class Roo.util.Observable
5732  * Base class that provides a common interface for publishing events. Subclasses are expected to
5733  * to have a property "events" with all the events defined.<br>
5734  * For example:
5735  * <pre><code>
5736  Employee = function(name){
5737     this.name = name;
5738     this.addEvents({
5739         "fired" : true,
5740         "quit" : true
5741     });
5742  }
5743  Roo.extend(Employee, Roo.util.Observable);
5744 </code></pre>
5745  * @param {Object} config properties to use (incuding events / listeners)
5746  */
5747
5748 Roo.util.Observable = function(cfg){
5749     
5750     cfg = cfg|| {};
5751     this.addEvents(cfg.events || {});
5752     if (cfg.events) {
5753         delete cfg.events; // make sure
5754     }
5755      
5756     Roo.apply(this, cfg);
5757     
5758     if(this.listeners){
5759         this.on(this.listeners);
5760         delete this.listeners;
5761     }
5762 };
5763 Roo.util.Observable.prototype = {
5764     /** 
5765  * @cfg {Object} listeners  list of events and functions to call for this object, 
5766  * For example :
5767  * <pre><code>
5768     listeners :  { 
5769        'click' : function(e) {
5770            ..... 
5771         } ,
5772         .... 
5773     } 
5774   </code></pre>
5775  */
5776     
5777     
5778     /**
5779      * Fires the specified event with the passed parameters (minus the event name).
5780      * @param {String} eventName
5781      * @param {Object...} args Variable number of parameters are passed to handlers
5782      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5783      */
5784     fireEvent : function(){
5785         var ce = this.events[arguments[0].toLowerCase()];
5786         if(typeof ce == "object"){
5787             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5788         }else{
5789             return true;
5790         }
5791     },
5792
5793     // private
5794     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5795
5796     /**
5797      * Appends an event handler to this component
5798      * @param {String}   eventName The type of event to listen for
5799      * @param {Function} handler The method the event invokes
5800      * @param {Object}   scope (optional) The scope in which to execute the handler
5801      * function. The handler function's "this" context.
5802      * @param {Object}   options (optional) An object containing handler configuration
5803      * properties. This may contain any of the following properties:<ul>
5804      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5805      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5806      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5807      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5808      * by the specified number of milliseconds. If the event fires again within that time, the original
5809      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5810      * </ul><br>
5811      * <p>
5812      * <b>Combining Options</b><br>
5813      * Using the options argument, it is possible to combine different types of listeners:<br>
5814      * <br>
5815      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5816                 <pre><code>
5817                 el.on('click', this.onClick, this, {
5818                         single: true,
5819                 delay: 100,
5820                 forumId: 4
5821                 });
5822                 </code></pre>
5823      * <p>
5824      * <b>Attaching multiple handlers in 1 call</b><br>
5825      * The method also allows for a single argument to be passed which is a config object containing properties
5826      * which specify multiple handlers.
5827      * <pre><code>
5828                 el.on({
5829                         'click': {
5830                         fn: this.onClick,
5831                         scope: this,
5832                         delay: 100
5833                 }, 
5834                 'mouseover': {
5835                         fn: this.onMouseOver,
5836                         scope: this
5837                 },
5838                 'mouseout': {
5839                         fn: this.onMouseOut,
5840                         scope: this
5841                 }
5842                 });
5843                 </code></pre>
5844      * <p>
5845      * Or a shorthand syntax which passes the same scope object to all handlers:
5846         <pre><code>
5847                 el.on({
5848                         'click': this.onClick,
5849                 'mouseover': this.onMouseOver,
5850                 'mouseout': this.onMouseOut,
5851                 scope: this
5852                 });
5853                 </code></pre>
5854      */
5855     addListener : function(eventName, fn, scope, o){
5856         if(typeof eventName == "object"){
5857             o = eventName;
5858             for(var e in o){
5859                 if(this.filterOptRe.test(e)){
5860                     continue;
5861                 }
5862                 if(typeof o[e] == "function"){
5863                     // shared options
5864                     this.addListener(e, o[e], o.scope,  o);
5865                 }else{
5866                     // individual options
5867                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5868                 }
5869             }
5870             return;
5871         }
5872         o = (!o || typeof o == "boolean") ? {} : o;
5873         eventName = eventName.toLowerCase();
5874         var ce = this.events[eventName] || true;
5875         if(typeof ce == "boolean"){
5876             ce = new Roo.util.Event(this, eventName);
5877             this.events[eventName] = ce;
5878         }
5879         ce.addListener(fn, scope, o);
5880     },
5881
5882     /**
5883      * Removes a listener
5884      * @param {String}   eventName     The type of event to listen for
5885      * @param {Function} handler        The handler to remove
5886      * @param {Object}   scope  (optional) The scope (this object) for the handler
5887      */
5888     removeListener : function(eventName, fn, scope){
5889         var ce = this.events[eventName.toLowerCase()];
5890         if(typeof ce == "object"){
5891             ce.removeListener(fn, scope);
5892         }
5893     },
5894
5895     /**
5896      * Removes all listeners for this object
5897      */
5898     purgeListeners : function(){
5899         for(var evt in this.events){
5900             if(typeof this.events[evt] == "object"){
5901                  this.events[evt].clearListeners();
5902             }
5903         }
5904     },
5905
5906     relayEvents : function(o, events){
5907         var createHandler = function(ename){
5908             return function(){
5909                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5910             };
5911         };
5912         for(var i = 0, len = events.length; i < len; i++){
5913             var ename = events[i];
5914             if(!this.events[ename]){ this.events[ename] = true; };
5915             o.on(ename, createHandler(ename), this);
5916         }
5917     },
5918
5919     /**
5920      * Used to define events on this Observable
5921      * @param {Object} object The object with the events defined
5922      */
5923     addEvents : function(o){
5924         if(!this.events){
5925             this.events = {};
5926         }
5927         Roo.applyIf(this.events, o);
5928     },
5929
5930     /**
5931      * Checks to see if this object has any listeners for a specified event
5932      * @param {String} eventName The name of the event to check for
5933      * @return {Boolean} True if the event is being listened for, else false
5934      */
5935     hasListener : function(eventName){
5936         var e = this.events[eventName];
5937         return typeof e == "object" && e.listeners.length > 0;
5938     }
5939 };
5940 /**
5941  * Appends an event handler to this element (shorthand for addListener)
5942  * @param {String}   eventName     The type of event to listen for
5943  * @param {Function} handler        The method the event invokes
5944  * @param {Object}   scope (optional) The scope in which to execute the handler
5945  * function. The handler function's "this" context.
5946  * @param {Object}   options  (optional)
5947  * @method
5948  */
5949 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5950 /**
5951  * Removes a listener (shorthand for removeListener)
5952  * @param {String}   eventName     The type of event to listen for
5953  * @param {Function} handler        The handler to remove
5954  * @param {Object}   scope  (optional) The scope (this object) for the handler
5955  * @method
5956  */
5957 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5958
5959 /**
5960  * Starts capture on the specified Observable. All events will be passed
5961  * to the supplied function with the event name + standard signature of the event
5962  * <b>before</b> the event is fired. If the supplied function returns false,
5963  * the event will not fire.
5964  * @param {Observable} o The Observable to capture
5965  * @param {Function} fn The function to call
5966  * @param {Object} scope (optional) The scope (this object) for the fn
5967  * @static
5968  */
5969 Roo.util.Observable.capture = function(o, fn, scope){
5970     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5971 };
5972
5973 /**
5974  * Removes <b>all</b> added captures from the Observable.
5975  * @param {Observable} o The Observable to release
5976  * @static
5977  */
5978 Roo.util.Observable.releaseCapture = function(o){
5979     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5980 };
5981
5982 (function(){
5983
5984     var createBuffered = function(h, o, scope){
5985         var task = new Roo.util.DelayedTask();
5986         return function(){
5987             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5988         };
5989     };
5990
5991     var createSingle = function(h, e, fn, scope){
5992         return function(){
5993             e.removeListener(fn, scope);
5994             return h.apply(scope, arguments);
5995         };
5996     };
5997
5998     var createDelayed = function(h, o, scope){
5999         return function(){
6000             var args = Array.prototype.slice.call(arguments, 0);
6001             setTimeout(function(){
6002                 h.apply(scope, args);
6003             }, o.delay || 10);
6004         };
6005     };
6006
6007     Roo.util.Event = function(obj, name){
6008         this.name = name;
6009         this.obj = obj;
6010         this.listeners = [];
6011     };
6012
6013     Roo.util.Event.prototype = {
6014         addListener : function(fn, scope, options){
6015             var o = options || {};
6016             scope = scope || this.obj;
6017             if(!this.isListening(fn, scope)){
6018                 var l = {fn: fn, scope: scope, options: o};
6019                 var h = fn;
6020                 if(o.delay){
6021                     h = createDelayed(h, o, scope);
6022                 }
6023                 if(o.single){
6024                     h = createSingle(h, this, fn, scope);
6025                 }
6026                 if(o.buffer){
6027                     h = createBuffered(h, o, scope);
6028                 }
6029                 l.fireFn = h;
6030                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6031                     this.listeners.push(l);
6032                 }else{
6033                     this.listeners = this.listeners.slice(0);
6034                     this.listeners.push(l);
6035                 }
6036             }
6037         },
6038
6039         findListener : function(fn, scope){
6040             scope = scope || this.obj;
6041             var ls = this.listeners;
6042             for(var i = 0, len = ls.length; i < len; i++){
6043                 var l = ls[i];
6044                 if(l.fn == fn && l.scope == scope){
6045                     return i;
6046                 }
6047             }
6048             return -1;
6049         },
6050
6051         isListening : function(fn, scope){
6052             return this.findListener(fn, scope) != -1;
6053         },
6054
6055         removeListener : function(fn, scope){
6056             var index;
6057             if((index = this.findListener(fn, scope)) != -1){
6058                 if(!this.firing){
6059                     this.listeners.splice(index, 1);
6060                 }else{
6061                     this.listeners = this.listeners.slice(0);
6062                     this.listeners.splice(index, 1);
6063                 }
6064                 return true;
6065             }
6066             return false;
6067         },
6068
6069         clearListeners : function(){
6070             this.listeners = [];
6071         },
6072
6073         fire : function(){
6074             var ls = this.listeners, scope, len = ls.length;
6075             if(len > 0){
6076                 this.firing = true;
6077                 var args = Array.prototype.slice.call(arguments, 0);
6078                 for(var i = 0; i < len; i++){
6079                     var l = ls[i];
6080                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6081                         this.firing = false;
6082                         return false;
6083                     }
6084                 }
6085                 this.firing = false;
6086             }
6087             return true;
6088         }
6089     };
6090 })();/*
6091  * RooJS Library 
6092  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6093  *
6094  * Licence LGPL 
6095  *
6096  */
6097  
6098 /**
6099  * @class Roo.Document
6100  * @extends Roo.util.Observable
6101  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6102  * 
6103  * @param {Object} config the methods and properties of the 'base' class for the application.
6104  * 
6105  *  Generic Page handler - implement this to start your app..
6106  * 
6107  * eg.
6108  *  MyProject = new Roo.Document({
6109         events : {
6110             'load' : true // your events..
6111         },
6112         listeners : {
6113             'ready' : function() {
6114                 // fired on Roo.onReady()
6115             }
6116         }
6117  * 
6118  */
6119 Roo.Document = function(cfg) {
6120      
6121     this.addEvents({ 
6122         'ready' : true
6123     });
6124     Roo.util.Observable.call(this,cfg);
6125     
6126     var _this = this;
6127     
6128     Roo.onReady(function() {
6129         _this.fireEvent('ready');
6130     },null,false);
6131     
6132     
6133 }
6134
6135 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6136  * Based on:
6137  * Ext JS Library 1.1.1
6138  * Copyright(c) 2006-2007, Ext JS, LLC.
6139  *
6140  * Originally Released Under LGPL - original licence link has changed is not relivant.
6141  *
6142  * Fork - LGPL
6143  * <script type="text/javascript">
6144  */
6145
6146 /**
6147  * @class Roo.EventManager
6148  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6149  * several useful events directly.
6150  * See {@link Roo.EventObject} for more details on normalized event objects.
6151  * @singleton
6152  */
6153 Roo.EventManager = function(){
6154     var docReadyEvent, docReadyProcId, docReadyState = false;
6155     var resizeEvent, resizeTask, textEvent, textSize;
6156     var E = Roo.lib.Event;
6157     var D = Roo.lib.Dom;
6158
6159     
6160     
6161
6162     var fireDocReady = function(){
6163         if(!docReadyState){
6164             docReadyState = true;
6165             Roo.isReady = true;
6166             if(docReadyProcId){
6167                 clearInterval(docReadyProcId);
6168             }
6169             if(Roo.isGecko || Roo.isOpera) {
6170                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6171             }
6172             if(Roo.isIE){
6173                 var defer = document.getElementById("ie-deferred-loader");
6174                 if(defer){
6175                     defer.onreadystatechange = null;
6176                     defer.parentNode.removeChild(defer);
6177                 }
6178             }
6179             if(docReadyEvent){
6180                 docReadyEvent.fire();
6181                 docReadyEvent.clearListeners();
6182             }
6183         }
6184     };
6185     
6186     var initDocReady = function(){
6187         docReadyEvent = new Roo.util.Event();
6188         if(Roo.isGecko || Roo.isOpera) {
6189             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6190         }else if(Roo.isIE){
6191             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6192             var defer = document.getElementById("ie-deferred-loader");
6193             defer.onreadystatechange = function(){
6194                 if(this.readyState == "complete"){
6195                     fireDocReady();
6196                 }
6197             };
6198         }else if(Roo.isSafari){ 
6199             docReadyProcId = setInterval(function(){
6200                 var rs = document.readyState;
6201                 if(rs == "complete") {
6202                     fireDocReady();     
6203                  }
6204             }, 10);
6205         }
6206         // no matter what, make sure it fires on load
6207         E.on(window, "load", fireDocReady);
6208     };
6209
6210     var createBuffered = function(h, o){
6211         var task = new Roo.util.DelayedTask(h);
6212         return function(e){
6213             // create new event object impl so new events don't wipe out properties
6214             e = new Roo.EventObjectImpl(e);
6215             task.delay(o.buffer, h, null, [e]);
6216         };
6217     };
6218
6219     var createSingle = function(h, el, ename, fn){
6220         return function(e){
6221             Roo.EventManager.removeListener(el, ename, fn);
6222             h(e);
6223         };
6224     };
6225
6226     var createDelayed = function(h, o){
6227         return function(e){
6228             // create new event object impl so new events don't wipe out properties
6229             e = new Roo.EventObjectImpl(e);
6230             setTimeout(function(){
6231                 h(e);
6232             }, o.delay || 10);
6233         };
6234     };
6235     var transitionEndVal = false;
6236     
6237     var transitionEnd = function()
6238     {
6239         if (transitionEndVal) {
6240             return transitionEndVal;
6241         }
6242         var el = document.createElement('div');
6243
6244         var transEndEventNames = {
6245             WebkitTransition : 'webkitTransitionEnd',
6246             MozTransition    : 'transitionend',
6247             OTransition      : 'oTransitionEnd otransitionend',
6248             transition       : 'transitionend'
6249         };
6250     
6251         for (var name in transEndEventNames) {
6252             if (el.style[name] !== undefined) {
6253                 transitionEndVal = transEndEventNames[name];
6254                 return  transitionEndVal ;
6255             }
6256         }
6257     }
6258     
6259
6260     var listen = function(element, ename, opt, fn, scope){
6261         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6262         fn = fn || o.fn; scope = scope || o.scope;
6263         var el = Roo.getDom(element);
6264         
6265         
6266         if(!el){
6267             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6268         }
6269         
6270         if (ename == 'transitionend') {
6271             ename = transitionEnd();
6272         }
6273         var h = function(e){
6274             e = Roo.EventObject.setEvent(e);
6275             var t;
6276             if(o.delegate){
6277                 t = e.getTarget(o.delegate, el);
6278                 if(!t){
6279                     return;
6280                 }
6281             }else{
6282                 t = e.target;
6283             }
6284             if(o.stopEvent === true){
6285                 e.stopEvent();
6286             }
6287             if(o.preventDefault === true){
6288                e.preventDefault();
6289             }
6290             if(o.stopPropagation === true){
6291                 e.stopPropagation();
6292             }
6293
6294             if(o.normalized === false){
6295                 e = e.browserEvent;
6296             }
6297
6298             fn.call(scope || el, e, t, o);
6299         };
6300         if(o.delay){
6301             h = createDelayed(h, o);
6302         }
6303         if(o.single){
6304             h = createSingle(h, el, ename, fn);
6305         }
6306         if(o.buffer){
6307             h = createBuffered(h, o);
6308         }
6309         
6310         fn._handlers = fn._handlers || [];
6311         
6312         
6313         fn._handlers.push([Roo.id(el), ename, h]);
6314         
6315         
6316          
6317         E.on(el, ename, h);
6318         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6319             el.addEventListener("DOMMouseScroll", h, false);
6320             E.on(window, 'unload', function(){
6321                 el.removeEventListener("DOMMouseScroll", h, false);
6322             });
6323         }
6324         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6325             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6326         }
6327         return h;
6328     };
6329
6330     var stopListening = function(el, ename, fn){
6331         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6332         if(hds){
6333             for(var i = 0, len = hds.length; i < len; i++){
6334                 var h = hds[i];
6335                 if(h[0] == id && h[1] == ename){
6336                     hd = h[2];
6337                     hds.splice(i, 1);
6338                     break;
6339                 }
6340             }
6341         }
6342         E.un(el, ename, hd);
6343         el = Roo.getDom(el);
6344         if(ename == "mousewheel" && el.addEventListener){
6345             el.removeEventListener("DOMMouseScroll", hd, false);
6346         }
6347         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6348             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6349         }
6350     };
6351
6352     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6353     
6354     var pub = {
6355         
6356         
6357         /** 
6358          * Fix for doc tools
6359          * @scope Roo.EventManager
6360          */
6361         
6362         
6363         /** 
6364          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6365          * object with a Roo.EventObject
6366          * @param {Function} fn        The method the event invokes
6367          * @param {Object}   scope    An object that becomes the scope of the handler
6368          * @param {boolean}  override If true, the obj passed in becomes
6369          *                             the execution scope of the listener
6370          * @return {Function} The wrapped function
6371          * @deprecated
6372          */
6373         wrap : function(fn, scope, override){
6374             return function(e){
6375                 Roo.EventObject.setEvent(e);
6376                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6377             };
6378         },
6379         
6380         /**
6381      * Appends an event handler to an element (shorthand for addListener)
6382      * @param {String/HTMLElement}   element        The html element or id to assign the
6383      * @param {String}   eventName The type of event to listen for
6384      * @param {Function} handler The method the event invokes
6385      * @param {Object}   scope (optional) The scope in which to execute the handler
6386      * function. The handler function's "this" context.
6387      * @param {Object}   options (optional) An object containing handler configuration
6388      * properties. This may contain any of the following properties:<ul>
6389      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6390      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6391      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6392      * <li>preventDefault {Boolean} True to prevent the default action</li>
6393      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6394      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6395      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6396      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6397      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6398      * by the specified number of milliseconds. If the event fires again within that time, the original
6399      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6400      * </ul><br>
6401      * <p>
6402      * <b>Combining Options</b><br>
6403      * Using the options argument, it is possible to combine different types of listeners:<br>
6404      * <br>
6405      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6406      * Code:<pre><code>
6407 el.on('click', this.onClick, this, {
6408     single: true,
6409     delay: 100,
6410     stopEvent : true,
6411     forumId: 4
6412 });</code></pre>
6413      * <p>
6414      * <b>Attaching multiple handlers in 1 call</b><br>
6415       * The method also allows for a single argument to be passed which is a config object containing properties
6416      * which specify multiple handlers.
6417      * <p>
6418      * Code:<pre><code>
6419 el.on({
6420     'click' : {
6421         fn: this.onClick
6422         scope: this,
6423         delay: 100
6424     },
6425     'mouseover' : {
6426         fn: this.onMouseOver
6427         scope: this
6428     },
6429     'mouseout' : {
6430         fn: this.onMouseOut
6431         scope: this
6432     }
6433 });</code></pre>
6434      * <p>
6435      * Or a shorthand syntax:<br>
6436      * Code:<pre><code>
6437 el.on({
6438     'click' : this.onClick,
6439     'mouseover' : this.onMouseOver,
6440     'mouseout' : this.onMouseOut
6441     scope: this
6442 });</code></pre>
6443      */
6444         addListener : function(element, eventName, fn, scope, options){
6445             if(typeof eventName == "object"){
6446                 var o = eventName;
6447                 for(var e in o){
6448                     if(propRe.test(e)){
6449                         continue;
6450                     }
6451                     if(typeof o[e] == "function"){
6452                         // shared options
6453                         listen(element, e, o, o[e], o.scope);
6454                     }else{
6455                         // individual options
6456                         listen(element, e, o[e]);
6457                     }
6458                 }
6459                 return;
6460             }
6461             return listen(element, eventName, options, fn, scope);
6462         },
6463         
6464         /**
6465          * Removes an event handler
6466          *
6467          * @param {String/HTMLElement}   element        The id or html element to remove the 
6468          *                             event from
6469          * @param {String}   eventName     The type of event
6470          * @param {Function} fn
6471          * @return {Boolean} True if a listener was actually removed
6472          */
6473         removeListener : function(element, eventName, fn){
6474             return stopListening(element, eventName, fn);
6475         },
6476         
6477         /**
6478          * Fires when the document is ready (before onload and before images are loaded). Can be 
6479          * accessed shorthanded Roo.onReady().
6480          * @param {Function} fn        The method the event invokes
6481          * @param {Object}   scope    An  object that becomes the scope of the handler
6482          * @param {boolean}  options
6483          */
6484         onDocumentReady : function(fn, scope, options){
6485             if(docReadyState){ // if it already fired
6486                 docReadyEvent.addListener(fn, scope, options);
6487                 docReadyEvent.fire();
6488                 docReadyEvent.clearListeners();
6489                 return;
6490             }
6491             if(!docReadyEvent){
6492                 initDocReady();
6493             }
6494             docReadyEvent.addListener(fn, scope, options);
6495         },
6496         
6497         /**
6498          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6499          * @param {Function} fn        The method the event invokes
6500          * @param {Object}   scope    An object that becomes the scope of the handler
6501          * @param {boolean}  options
6502          */
6503         onWindowResize : function(fn, scope, options){
6504             if(!resizeEvent){
6505                 resizeEvent = new Roo.util.Event();
6506                 resizeTask = new Roo.util.DelayedTask(function(){
6507                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6508                 });
6509                 E.on(window, "resize", function(){
6510                     if(Roo.isIE){
6511                         resizeTask.delay(50);
6512                     }else{
6513                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6514                     }
6515                 });
6516             }
6517             resizeEvent.addListener(fn, scope, options);
6518         },
6519
6520         /**
6521          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6522          * @param {Function} fn        The method the event invokes
6523          * @param {Object}   scope    An object that becomes the scope of the handler
6524          * @param {boolean}  options
6525          */
6526         onTextResize : function(fn, scope, options){
6527             if(!textEvent){
6528                 textEvent = new Roo.util.Event();
6529                 var textEl = new Roo.Element(document.createElement('div'));
6530                 textEl.dom.className = 'x-text-resize';
6531                 textEl.dom.innerHTML = 'X';
6532                 textEl.appendTo(document.body);
6533                 textSize = textEl.dom.offsetHeight;
6534                 setInterval(function(){
6535                     if(textEl.dom.offsetHeight != textSize){
6536                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6537                     }
6538                 }, this.textResizeInterval);
6539             }
6540             textEvent.addListener(fn, scope, options);
6541         },
6542
6543         /**
6544          * Removes the passed window resize listener.
6545          * @param {Function} fn        The method the event invokes
6546          * @param {Object}   scope    The scope of handler
6547          */
6548         removeResizeListener : function(fn, scope){
6549             if(resizeEvent){
6550                 resizeEvent.removeListener(fn, scope);
6551             }
6552         },
6553
6554         // private
6555         fireResize : function(){
6556             if(resizeEvent){
6557                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6558             }   
6559         },
6560         /**
6561          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6562          */
6563         ieDeferSrc : false,
6564         /**
6565          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6566          */
6567         textResizeInterval : 50
6568     };
6569     
6570     /**
6571      * Fix for doc tools
6572      * @scopeAlias pub=Roo.EventManager
6573      */
6574     
6575      /**
6576      * Appends an event handler to an element (shorthand for addListener)
6577      * @param {String/HTMLElement}   element        The html element or id to assign the
6578      * @param {String}   eventName The type of event to listen for
6579      * @param {Function} handler The method the event invokes
6580      * @param {Object}   scope (optional) The scope in which to execute the handler
6581      * function. The handler function's "this" context.
6582      * @param {Object}   options (optional) An object containing handler configuration
6583      * properties. This may contain any of the following properties:<ul>
6584      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6585      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6586      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6587      * <li>preventDefault {Boolean} True to prevent the default action</li>
6588      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6589      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6590      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6591      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6592      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6593      * by the specified number of milliseconds. If the event fires again within that time, the original
6594      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6595      * </ul><br>
6596      * <p>
6597      * <b>Combining Options</b><br>
6598      * Using the options argument, it is possible to combine different types of listeners:<br>
6599      * <br>
6600      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6601      * Code:<pre><code>
6602 el.on('click', this.onClick, this, {
6603     single: true,
6604     delay: 100,
6605     stopEvent : true,
6606     forumId: 4
6607 });</code></pre>
6608      * <p>
6609      * <b>Attaching multiple handlers in 1 call</b><br>
6610       * The method also allows for a single argument to be passed which is a config object containing properties
6611      * which specify multiple handlers.
6612      * <p>
6613      * Code:<pre><code>
6614 el.on({
6615     'click' : {
6616         fn: this.onClick
6617         scope: this,
6618         delay: 100
6619     },
6620     'mouseover' : {
6621         fn: this.onMouseOver
6622         scope: this
6623     },
6624     'mouseout' : {
6625         fn: this.onMouseOut
6626         scope: this
6627     }
6628 });</code></pre>
6629      * <p>
6630      * Or a shorthand syntax:<br>
6631      * Code:<pre><code>
6632 el.on({
6633     'click' : this.onClick,
6634     'mouseover' : this.onMouseOver,
6635     'mouseout' : this.onMouseOut
6636     scope: this
6637 });</code></pre>
6638      */
6639     pub.on = pub.addListener;
6640     pub.un = pub.removeListener;
6641
6642     pub.stoppedMouseDownEvent = new Roo.util.Event();
6643     return pub;
6644 }();
6645 /**
6646   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6647   * @param {Function} fn        The method the event invokes
6648   * @param {Object}   scope    An  object that becomes the scope of the handler
6649   * @param {boolean}  override If true, the obj passed in becomes
6650   *                             the execution scope of the listener
6651   * @member Roo
6652   * @method onReady
6653  */
6654 Roo.onReady = Roo.EventManager.onDocumentReady;
6655
6656 Roo.onReady(function(){
6657     var bd = Roo.get(document.body);
6658     if(!bd){ return; }
6659
6660     var cls = [
6661             Roo.isIE ? "roo-ie"
6662             : Roo.isIE11 ? "roo-ie11"
6663             : Roo.isEdge ? "roo-edge"
6664             : Roo.isGecko ? "roo-gecko"
6665             : Roo.isOpera ? "roo-opera"
6666             : Roo.isSafari ? "roo-safari" : ""];
6667
6668     if(Roo.isMac){
6669         cls.push("roo-mac");
6670     }
6671     if(Roo.isLinux){
6672         cls.push("roo-linux");
6673     }
6674     if(Roo.isIOS){
6675         cls.push("roo-ios");
6676     }
6677     if(Roo.isTouch){
6678         cls.push("roo-touch");
6679     }
6680     if(Roo.isBorderBox){
6681         cls.push('roo-border-box');
6682     }
6683     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6684         var p = bd.dom.parentNode;
6685         if(p){
6686             p.className += ' roo-strict';
6687         }
6688     }
6689     bd.addClass(cls.join(' '));
6690 });
6691
6692 /**
6693  * @class Roo.EventObject
6694  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6695  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6696  * Example:
6697  * <pre><code>
6698  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6699     e.preventDefault();
6700     var target = e.getTarget();
6701     ...
6702  }
6703  var myDiv = Roo.get("myDiv");
6704  myDiv.on("click", handleClick);
6705  //or
6706  Roo.EventManager.on("myDiv", 'click', handleClick);
6707  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6708  </code></pre>
6709  * @singleton
6710  */
6711 Roo.EventObject = function(){
6712     
6713     var E = Roo.lib.Event;
6714     
6715     // safari keypress events for special keys return bad keycodes
6716     var safariKeys = {
6717         63234 : 37, // left
6718         63235 : 39, // right
6719         63232 : 38, // up
6720         63233 : 40, // down
6721         63276 : 33, // page up
6722         63277 : 34, // page down
6723         63272 : 46, // delete
6724         63273 : 36, // home
6725         63275 : 35  // end
6726     };
6727
6728     // normalize button clicks
6729     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6730                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6731
6732     Roo.EventObjectImpl = function(e){
6733         if(e){
6734             this.setEvent(e.browserEvent || e);
6735         }
6736     };
6737     Roo.EventObjectImpl.prototype = {
6738         /**
6739          * Used to fix doc tools.
6740          * @scope Roo.EventObject.prototype
6741          */
6742             
6743
6744         
6745         
6746         /** The normal browser event */
6747         browserEvent : null,
6748         /** The button pressed in a mouse event */
6749         button : -1,
6750         /** True if the shift key was down during the event */
6751         shiftKey : false,
6752         /** True if the control key was down during the event */
6753         ctrlKey : false,
6754         /** True if the alt key was down during the event */
6755         altKey : false,
6756
6757         /** Key constant 
6758         * @type Number */
6759         BACKSPACE : 8,
6760         /** Key constant 
6761         * @type Number */
6762         TAB : 9,
6763         /** Key constant 
6764         * @type Number */
6765         RETURN : 13,
6766         /** Key constant 
6767         * @type Number */
6768         ENTER : 13,
6769         /** Key constant 
6770         * @type Number */
6771         SHIFT : 16,
6772         /** Key constant 
6773         * @type Number */
6774         CONTROL : 17,
6775         /** Key constant 
6776         * @type Number */
6777         ESC : 27,
6778         /** Key constant 
6779         * @type Number */
6780         SPACE : 32,
6781         /** Key constant 
6782         * @type Number */
6783         PAGEUP : 33,
6784         /** Key constant 
6785         * @type Number */
6786         PAGEDOWN : 34,
6787         /** Key constant 
6788         * @type Number */
6789         END : 35,
6790         /** Key constant 
6791         * @type Number */
6792         HOME : 36,
6793         /** Key constant 
6794         * @type Number */
6795         LEFT : 37,
6796         /** Key constant 
6797         * @type Number */
6798         UP : 38,
6799         /** Key constant 
6800         * @type Number */
6801         RIGHT : 39,
6802         /** Key constant 
6803         * @type Number */
6804         DOWN : 40,
6805         /** Key constant 
6806         * @type Number */
6807         DELETE : 46,
6808         /** Key constant 
6809         * @type Number */
6810         F5 : 116,
6811
6812            /** @private */
6813         setEvent : function(e){
6814             if(e == this || (e && e.browserEvent)){ // already wrapped
6815                 return e;
6816             }
6817             this.browserEvent = e;
6818             if(e){
6819                 // normalize buttons
6820                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6821                 if(e.type == 'click' && this.button == -1){
6822                     this.button = 0;
6823                 }
6824                 this.type = e.type;
6825                 this.shiftKey = e.shiftKey;
6826                 // mac metaKey behaves like ctrlKey
6827                 this.ctrlKey = e.ctrlKey || e.metaKey;
6828                 this.altKey = e.altKey;
6829                 // in getKey these will be normalized for the mac
6830                 this.keyCode = e.keyCode;
6831                 // keyup warnings on firefox.
6832                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6833                 // cache the target for the delayed and or buffered events
6834                 this.target = E.getTarget(e);
6835                 // same for XY
6836                 this.xy = E.getXY(e);
6837             }else{
6838                 this.button = -1;
6839                 this.shiftKey = false;
6840                 this.ctrlKey = false;
6841                 this.altKey = false;
6842                 this.keyCode = 0;
6843                 this.charCode =0;
6844                 this.target = null;
6845                 this.xy = [0, 0];
6846             }
6847             return this;
6848         },
6849
6850         /**
6851          * Stop the event (preventDefault and stopPropagation)
6852          */
6853         stopEvent : function(){
6854             if(this.browserEvent){
6855                 if(this.browserEvent.type == 'mousedown'){
6856                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6857                 }
6858                 E.stopEvent(this.browserEvent);
6859             }
6860         },
6861
6862         /**
6863          * Prevents the browsers default handling of the event.
6864          */
6865         preventDefault : function(){
6866             if(this.browserEvent){
6867                 E.preventDefault(this.browserEvent);
6868             }
6869         },
6870
6871         /** @private */
6872         isNavKeyPress : function(){
6873             var k = this.keyCode;
6874             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6875             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6876         },
6877
6878         isSpecialKey : function(){
6879             var k = this.keyCode;
6880             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6881             (k == 16) || (k == 17) ||
6882             (k >= 18 && k <= 20) ||
6883             (k >= 33 && k <= 35) ||
6884             (k >= 36 && k <= 39) ||
6885             (k >= 44 && k <= 45);
6886         },
6887         /**
6888          * Cancels bubbling of the event.
6889          */
6890         stopPropagation : function(){
6891             if(this.browserEvent){
6892                 if(this.type == 'mousedown'){
6893                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6894                 }
6895                 E.stopPropagation(this.browserEvent);
6896             }
6897         },
6898
6899         /**
6900          * Gets the key code for the event.
6901          * @return {Number}
6902          */
6903         getCharCode : function(){
6904             return this.charCode || this.keyCode;
6905         },
6906
6907         /**
6908          * Returns a normalized keyCode for the event.
6909          * @return {Number} The key code
6910          */
6911         getKey : function(){
6912             var k = this.keyCode || this.charCode;
6913             return Roo.isSafari ? (safariKeys[k] || k) : k;
6914         },
6915
6916         /**
6917          * Gets the x coordinate of the event.
6918          * @return {Number}
6919          */
6920         getPageX : function(){
6921             return this.xy[0];
6922         },
6923
6924         /**
6925          * Gets the y coordinate of the event.
6926          * @return {Number}
6927          */
6928         getPageY : function(){
6929             return this.xy[1];
6930         },
6931
6932         /**
6933          * Gets the time of the event.
6934          * @return {Number}
6935          */
6936         getTime : function(){
6937             if(this.browserEvent){
6938                 return E.getTime(this.browserEvent);
6939             }
6940             return null;
6941         },
6942
6943         /**
6944          * Gets the page coordinates of the event.
6945          * @return {Array} The xy values like [x, y]
6946          */
6947         getXY : function(){
6948             return this.xy;
6949         },
6950
6951         /**
6952          * Gets the target for the event.
6953          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6954          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6955                 search as a number or element (defaults to 10 || document.body)
6956          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6957          * @return {HTMLelement}
6958          */
6959         getTarget : function(selector, maxDepth, returnEl){
6960             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6961         },
6962         /**
6963          * Gets the related target.
6964          * @return {HTMLElement}
6965          */
6966         getRelatedTarget : function(){
6967             if(this.browserEvent){
6968                 return E.getRelatedTarget(this.browserEvent);
6969             }
6970             return null;
6971         },
6972
6973         /**
6974          * Normalizes mouse wheel delta across browsers
6975          * @return {Number} The delta
6976          */
6977         getWheelDelta : function(){
6978             var e = this.browserEvent;
6979             var delta = 0;
6980             if(e.wheelDelta){ /* IE/Opera. */
6981                 delta = e.wheelDelta/120;
6982             }else if(e.detail){ /* Mozilla case. */
6983                 delta = -e.detail/3;
6984             }
6985             return delta;
6986         },
6987
6988         /**
6989          * Returns true if the control, meta, shift or alt key was pressed during this event.
6990          * @return {Boolean}
6991          */
6992         hasModifier : function(){
6993             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6994         },
6995
6996         /**
6997          * Returns true if the target of this event equals el or is a child of el
6998          * @param {String/HTMLElement/Element} el
6999          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7000          * @return {Boolean}
7001          */
7002         within : function(el, related){
7003             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7004             return t && Roo.fly(el).contains(t);
7005         },
7006
7007         getPoint : function(){
7008             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7009         }
7010     };
7011
7012     return new Roo.EventObjectImpl();
7013 }();
7014             
7015     /*
7016  * Based on:
7017  * Ext JS Library 1.1.1
7018  * Copyright(c) 2006-2007, Ext JS, LLC.
7019  *
7020  * Originally Released Under LGPL - original licence link has changed is not relivant.
7021  *
7022  * Fork - LGPL
7023  * <script type="text/javascript">
7024  */
7025
7026  
7027 // was in Composite Element!??!?!
7028  
7029 (function(){
7030     var D = Roo.lib.Dom;
7031     var E = Roo.lib.Event;
7032     var A = Roo.lib.Anim;
7033
7034     // local style camelizing for speed
7035     var propCache = {};
7036     var camelRe = /(-[a-z])/gi;
7037     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7038     var view = document.defaultView;
7039
7040 /**
7041  * @class Roo.Element
7042  * Represents an Element in the DOM.<br><br>
7043  * Usage:<br>
7044 <pre><code>
7045 var el = Roo.get("my-div");
7046
7047 // or with getEl
7048 var el = getEl("my-div");
7049
7050 // or with a DOM element
7051 var el = Roo.get(myDivElement);
7052 </code></pre>
7053  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7054  * each call instead of constructing a new one.<br><br>
7055  * <b>Animations</b><br />
7056  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7057  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7058 <pre>
7059 Option    Default   Description
7060 --------- --------  ---------------------------------------------
7061 duration  .35       The duration of the animation in seconds
7062 easing    easeOut   The YUI easing method
7063 callback  none      A function to execute when the anim completes
7064 scope     this      The scope (this) of the callback function
7065 </pre>
7066 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7067 * manipulate the animation. Here's an example:
7068 <pre><code>
7069 var el = Roo.get("my-div");
7070
7071 // no animation
7072 el.setWidth(100);
7073
7074 // default animation
7075 el.setWidth(100, true);
7076
7077 // animation with some options set
7078 el.setWidth(100, {
7079     duration: 1,
7080     callback: this.foo,
7081     scope: this
7082 });
7083
7084 // using the "anim" property to get the Anim object
7085 var opt = {
7086     duration: 1,
7087     callback: this.foo,
7088     scope: this
7089 };
7090 el.setWidth(100, opt);
7091 ...
7092 if(opt.anim.isAnimated()){
7093     opt.anim.stop();
7094 }
7095 </code></pre>
7096 * <b> Composite (Collections of) Elements</b><br />
7097  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7098  * @constructor Create a new Element directly.
7099  * @param {String/HTMLElement} element
7100  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7101  */
7102     Roo.Element = function(element, forceNew){
7103         var dom = typeof element == "string" ?
7104                 document.getElementById(element) : element;
7105         if(!dom){ // invalid id/element
7106             return null;
7107         }
7108         var id = dom.id;
7109         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7110             return Roo.Element.cache[id];
7111         }
7112
7113         /**
7114          * The DOM element
7115          * @type HTMLElement
7116          */
7117         this.dom = dom;
7118
7119         /**
7120          * The DOM element ID
7121          * @type String
7122          */
7123         this.id = id || Roo.id(dom);
7124     };
7125
7126     var El = Roo.Element;
7127
7128     El.prototype = {
7129         /**
7130          * The element's default display mode  (defaults to "")
7131          * @type String
7132          */
7133         originalDisplay : "",
7134
7135         visibilityMode : 1,
7136         /**
7137          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7138          * @type String
7139          */
7140         defaultUnit : "px",
7141         
7142         /**
7143          * Sets the element's visibility mode. When setVisible() is called it
7144          * will use this to determine whether to set the visibility or the display property.
7145          * @param visMode Element.VISIBILITY or Element.DISPLAY
7146          * @return {Roo.Element} this
7147          */
7148         setVisibilityMode : function(visMode){
7149             this.visibilityMode = visMode;
7150             return this;
7151         },
7152         /**
7153          * Convenience method for setVisibilityMode(Element.DISPLAY)
7154          * @param {String} display (optional) What to set display to when visible
7155          * @return {Roo.Element} this
7156          */
7157         enableDisplayMode : function(display){
7158             this.setVisibilityMode(El.DISPLAY);
7159             if(typeof display != "undefined") { this.originalDisplay = display; }
7160             return this;
7161         },
7162
7163         /**
7164          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7165          * @param {String} selector The simple selector to test
7166          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7167                 search as a number or element (defaults to 10 || document.body)
7168          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7169          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7170          */
7171         findParent : function(simpleSelector, maxDepth, returnEl){
7172             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7173             maxDepth = maxDepth || 50;
7174             if(typeof maxDepth != "number"){
7175                 stopEl = Roo.getDom(maxDepth);
7176                 maxDepth = 10;
7177             }
7178             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7179                 if(dq.is(p, simpleSelector)){
7180                     return returnEl ? Roo.get(p) : p;
7181                 }
7182                 depth++;
7183                 p = p.parentNode;
7184             }
7185             return null;
7186         },
7187
7188
7189         /**
7190          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7191          * @param {String} selector The simple selector to test
7192          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7193                 search as a number or element (defaults to 10 || document.body)
7194          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7195          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7196          */
7197         findParentNode : function(simpleSelector, maxDepth, returnEl){
7198             var p = Roo.fly(this.dom.parentNode, '_internal');
7199             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7200         },
7201         
7202         /**
7203          * Looks at  the scrollable parent element
7204          */
7205         findScrollableParent : function()
7206         {
7207             var overflowRegex = /(auto|scroll)/;
7208             
7209             if(this.getStyle('position') === 'fixed'){
7210                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7211             }
7212             
7213             var excludeStaticParent = this.getStyle('position') === "absolute";
7214             
7215             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7216                 
7217                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7218                     continue;
7219                 }
7220                 
7221                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7222                     return parent;
7223                 }
7224                 
7225                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7226                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7227                 }
7228             }
7229             
7230             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7231         },
7232
7233         /**
7234          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7235          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7236          * @param {String} selector The simple selector to test
7237          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7238                 search as a number or element (defaults to 10 || document.body)
7239          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7240          */
7241         up : function(simpleSelector, maxDepth){
7242             return this.findParentNode(simpleSelector, maxDepth, true);
7243         },
7244
7245
7246
7247         /**
7248          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7249          * @param {String} selector The simple selector to test
7250          * @return {Boolean} True if this element matches the selector, else false
7251          */
7252         is : function(simpleSelector){
7253             return Roo.DomQuery.is(this.dom, simpleSelector);
7254         },
7255
7256         /**
7257          * Perform animation on this element.
7258          * @param {Object} args The YUI animation control args
7259          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7260          * @param {Function} onComplete (optional) Function to call when animation completes
7261          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7262          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7263          * @return {Roo.Element} this
7264          */
7265         animate : function(args, duration, onComplete, easing, animType){
7266             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7267             return this;
7268         },
7269
7270         /*
7271          * @private Internal animation call
7272          */
7273         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7274             animType = animType || 'run';
7275             opt = opt || {};
7276             var anim = Roo.lib.Anim[animType](
7277                 this.dom, args,
7278                 (opt.duration || defaultDur) || .35,
7279                 (opt.easing || defaultEase) || 'easeOut',
7280                 function(){
7281                     Roo.callback(cb, this);
7282                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7283                 },
7284                 this
7285             );
7286             opt.anim = anim;
7287             return anim;
7288         },
7289
7290         // private legacy anim prep
7291         preanim : function(a, i){
7292             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7293         },
7294
7295         /**
7296          * Removes worthless text nodes
7297          * @param {Boolean} forceReclean (optional) By default the element
7298          * keeps track if it has been cleaned already so
7299          * you can call this over and over. However, if you update the element and
7300          * need to force a reclean, you can pass true.
7301          */
7302         clean : function(forceReclean){
7303             if(this.isCleaned && forceReclean !== true){
7304                 return this;
7305             }
7306             var ns = /\S/;
7307             var d = this.dom, n = d.firstChild, ni = -1;
7308             while(n){
7309                 var nx = n.nextSibling;
7310                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7311                     d.removeChild(n);
7312                 }else{
7313                     n.nodeIndex = ++ni;
7314                 }
7315                 n = nx;
7316             }
7317             this.isCleaned = true;
7318             return this;
7319         },
7320
7321         // private
7322         calcOffsetsTo : function(el){
7323             el = Roo.get(el);
7324             var d = el.dom;
7325             var restorePos = false;
7326             if(el.getStyle('position') == 'static'){
7327                 el.position('relative');
7328                 restorePos = true;
7329             }
7330             var x = 0, y =0;
7331             var op = this.dom;
7332             while(op && op != d && op.tagName != 'HTML'){
7333                 x+= op.offsetLeft;
7334                 y+= op.offsetTop;
7335                 op = op.offsetParent;
7336             }
7337             if(restorePos){
7338                 el.position('static');
7339             }
7340             return [x, y];
7341         },
7342
7343         /**
7344          * Scrolls this element into view within the passed container.
7345          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7346          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7347          * @return {Roo.Element} this
7348          */
7349         scrollIntoView : function(container, hscroll){
7350             var c = Roo.getDom(container) || document.body;
7351             var el = this.dom;
7352
7353             var o = this.calcOffsetsTo(c),
7354                 l = o[0],
7355                 t = o[1],
7356                 b = t+el.offsetHeight,
7357                 r = l+el.offsetWidth;
7358
7359             var ch = c.clientHeight;
7360             var ct = parseInt(c.scrollTop, 10);
7361             var cl = parseInt(c.scrollLeft, 10);
7362             var cb = ct + ch;
7363             var cr = cl + c.clientWidth;
7364
7365             if(t < ct){
7366                 c.scrollTop = t;
7367             }else if(b > cb){
7368                 c.scrollTop = b-ch;
7369             }
7370
7371             if(hscroll !== false){
7372                 if(l < cl){
7373                     c.scrollLeft = l;
7374                 }else if(r > cr){
7375                     c.scrollLeft = r-c.clientWidth;
7376                 }
7377             }
7378             return this;
7379         },
7380
7381         // private
7382         scrollChildIntoView : function(child, hscroll){
7383             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7384         },
7385
7386         /**
7387          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7388          * the new height may not be available immediately.
7389          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7390          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7391          * @param {Function} onComplete (optional) Function to call when animation completes
7392          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7393          * @return {Roo.Element} this
7394          */
7395         autoHeight : function(animate, duration, onComplete, easing){
7396             var oldHeight = this.getHeight();
7397             this.clip();
7398             this.setHeight(1); // force clipping
7399             setTimeout(function(){
7400                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7401                 if(!animate){
7402                     this.setHeight(height);
7403                     this.unclip();
7404                     if(typeof onComplete == "function"){
7405                         onComplete();
7406                     }
7407                 }else{
7408                     this.setHeight(oldHeight); // restore original height
7409                     this.setHeight(height, animate, duration, function(){
7410                         this.unclip();
7411                         if(typeof onComplete == "function") { onComplete(); }
7412                     }.createDelegate(this), easing);
7413                 }
7414             }.createDelegate(this), 0);
7415             return this;
7416         },
7417
7418         /**
7419          * Returns true if this element is an ancestor of the passed element
7420          * @param {HTMLElement/String} el The element to check
7421          * @return {Boolean} True if this element is an ancestor of el, else false
7422          */
7423         contains : function(el){
7424             if(!el){return false;}
7425             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7426         },
7427
7428         /**
7429          * Checks whether the element is currently visible using both visibility and display properties.
7430          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7431          * @return {Boolean} True if the element is currently visible, else false
7432          */
7433         isVisible : function(deep) {
7434             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7435             if(deep !== true || !vis){
7436                 return vis;
7437             }
7438             var p = this.dom.parentNode;
7439             while(p && p.tagName.toLowerCase() != "body"){
7440                 if(!Roo.fly(p, '_isVisible').isVisible()){
7441                     return false;
7442                 }
7443                 p = p.parentNode;
7444             }
7445             return true;
7446         },
7447
7448         /**
7449          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7450          * @param {String} selector The CSS selector
7451          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7452          * @return {CompositeElement/CompositeElementLite} The composite element
7453          */
7454         select : function(selector, unique){
7455             return El.select(selector, unique, this.dom);
7456         },
7457
7458         /**
7459          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7460          * @param {String} selector The CSS selector
7461          * @return {Array} An array of the matched nodes
7462          */
7463         query : function(selector, unique){
7464             return Roo.DomQuery.select(selector, this.dom);
7465         },
7466
7467         /**
7468          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7469          * @param {String} selector The CSS selector
7470          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7471          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7472          */
7473         child : function(selector, returnDom){
7474             var n = Roo.DomQuery.selectNode(selector, this.dom);
7475             return returnDom ? n : Roo.get(n);
7476         },
7477
7478         /**
7479          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7480          * @param {String} selector The CSS selector
7481          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7482          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7483          */
7484         down : function(selector, returnDom){
7485             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7486             return returnDom ? n : Roo.get(n);
7487         },
7488
7489         /**
7490          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7491          * @param {String} group The group the DD object is member of
7492          * @param {Object} config The DD config object
7493          * @param {Object} overrides An object containing methods to override/implement on the DD object
7494          * @return {Roo.dd.DD} The DD object
7495          */
7496         initDD : function(group, config, overrides){
7497             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7498             return Roo.apply(dd, overrides);
7499         },
7500
7501         /**
7502          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7503          * @param {String} group The group the DDProxy object is member of
7504          * @param {Object} config The DDProxy config object
7505          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7506          * @return {Roo.dd.DDProxy} The DDProxy object
7507          */
7508         initDDProxy : function(group, config, overrides){
7509             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7510             return Roo.apply(dd, overrides);
7511         },
7512
7513         /**
7514          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7515          * @param {String} group The group the DDTarget object is member of
7516          * @param {Object} config The DDTarget config object
7517          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7518          * @return {Roo.dd.DDTarget} The DDTarget object
7519          */
7520         initDDTarget : function(group, config, overrides){
7521             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7522             return Roo.apply(dd, overrides);
7523         },
7524
7525         /**
7526          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7527          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7528          * @param {Boolean} visible Whether the element is visible
7529          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7530          * @return {Roo.Element} this
7531          */
7532          setVisible : function(visible, animate){
7533             if(!animate || !A){
7534                 if(this.visibilityMode == El.DISPLAY){
7535                     this.setDisplayed(visible);
7536                 }else{
7537                     this.fixDisplay();
7538                     this.dom.style.visibility = visible ? "visible" : "hidden";
7539                 }
7540             }else{
7541                 // closure for composites
7542                 var dom = this.dom;
7543                 var visMode = this.visibilityMode;
7544                 if(visible){
7545                     this.setOpacity(.01);
7546                     this.setVisible(true);
7547                 }
7548                 this.anim({opacity: { to: (visible?1:0) }},
7549                       this.preanim(arguments, 1),
7550                       null, .35, 'easeIn', function(){
7551                          if(!visible){
7552                              if(visMode == El.DISPLAY){
7553                                  dom.style.display = "none";
7554                              }else{
7555                                  dom.style.visibility = "hidden";
7556                              }
7557                              Roo.get(dom).setOpacity(1);
7558                          }
7559                      });
7560             }
7561             return this;
7562         },
7563
7564         /**
7565          * Returns true if display is not "none"
7566          * @return {Boolean}
7567          */
7568         isDisplayed : function() {
7569             return this.getStyle("display") != "none";
7570         },
7571
7572         /**
7573          * Toggles the element's visibility or display, depending on visibility mode.
7574          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7575          * @return {Roo.Element} this
7576          */
7577         toggle : function(animate){
7578             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7579             return this;
7580         },
7581
7582         /**
7583          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7584          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7585          * @return {Roo.Element} this
7586          */
7587         setDisplayed : function(value) {
7588             if(typeof value == "boolean"){
7589                value = value ? this.originalDisplay : "none";
7590             }
7591             this.setStyle("display", value);
7592             return this;
7593         },
7594
7595         /**
7596          * Tries to focus the element. Any exceptions are caught and ignored.
7597          * @return {Roo.Element} this
7598          */
7599         focus : function() {
7600             try{
7601                 this.dom.focus();
7602             }catch(e){}
7603             return this;
7604         },
7605
7606         /**
7607          * Tries to blur the element. Any exceptions are caught and ignored.
7608          * @return {Roo.Element} this
7609          */
7610         blur : function() {
7611             try{
7612                 this.dom.blur();
7613             }catch(e){}
7614             return this;
7615         },
7616
7617         /**
7618          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7619          * @param {String/Array} className The CSS class to add, or an array of classes
7620          * @return {Roo.Element} this
7621          */
7622         addClass : function(className){
7623             if(className instanceof Array){
7624                 for(var i = 0, len = className.length; i < len; i++) {
7625                     this.addClass(className[i]);
7626                 }
7627             }else{
7628                 if(className && !this.hasClass(className)){
7629                     this.dom.className = this.dom.className + " " + className;
7630                 }
7631             }
7632             return this;
7633         },
7634
7635         /**
7636          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7637          * @param {String/Array} className The CSS class to add, or an array of classes
7638          * @return {Roo.Element} this
7639          */
7640         radioClass : function(className){
7641             var siblings = this.dom.parentNode.childNodes;
7642             for(var i = 0; i < siblings.length; i++) {
7643                 var s = siblings[i];
7644                 if(s.nodeType == 1){
7645                     Roo.get(s).removeClass(className);
7646                 }
7647             }
7648             this.addClass(className);
7649             return this;
7650         },
7651
7652         /**
7653          * Removes one or more CSS classes from the element.
7654          * @param {String/Array} className The CSS class to remove, or an array of classes
7655          * @return {Roo.Element} this
7656          */
7657         removeClass : function(className){
7658             if(!className || !this.dom.className){
7659                 return this;
7660             }
7661             if(className instanceof Array){
7662                 for(var i = 0, len = className.length; i < len; i++) {
7663                     this.removeClass(className[i]);
7664                 }
7665             }else{
7666                 if(this.hasClass(className)){
7667                     var re = this.classReCache[className];
7668                     if (!re) {
7669                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7670                        this.classReCache[className] = re;
7671                     }
7672                     this.dom.className =
7673                         this.dom.className.replace(re, " ");
7674                 }
7675             }
7676             return this;
7677         },
7678
7679         // private
7680         classReCache: {},
7681
7682         /**
7683          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7684          * @param {String} className The CSS class to toggle
7685          * @return {Roo.Element} this
7686          */
7687         toggleClass : function(className){
7688             if(this.hasClass(className)){
7689                 this.removeClass(className);
7690             }else{
7691                 this.addClass(className);
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Checks if the specified CSS class exists on this element's DOM node.
7698          * @param {String} className The CSS class to check for
7699          * @return {Boolean} True if the class exists, else false
7700          */
7701         hasClass : function(className){
7702             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7703         },
7704
7705         /**
7706          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7707          * @param {String} oldClassName The CSS class to replace
7708          * @param {String} newClassName The replacement CSS class
7709          * @return {Roo.Element} this
7710          */
7711         replaceClass : function(oldClassName, newClassName){
7712             this.removeClass(oldClassName);
7713             this.addClass(newClassName);
7714             return this;
7715         },
7716
7717         /**
7718          * Returns an object with properties matching the styles requested.
7719          * For example, el.getStyles('color', 'font-size', 'width') might return
7720          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7721          * @param {String} style1 A style name
7722          * @param {String} style2 A style name
7723          * @param {String} etc.
7724          * @return {Object} The style object
7725          */
7726         getStyles : function(){
7727             var a = arguments, len = a.length, r = {};
7728             for(var i = 0; i < len; i++){
7729                 r[a[i]] = this.getStyle(a[i]);
7730             }
7731             return r;
7732         },
7733
7734         /**
7735          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7736          * @param {String} property The style property whose value is returned.
7737          * @return {String} The current value of the style property for this element.
7738          */
7739         getStyle : function(){
7740             return view && view.getComputedStyle ?
7741                 function(prop){
7742                     var el = this.dom, v, cs, camel;
7743                     if(prop == 'float'){
7744                         prop = "cssFloat";
7745                     }
7746                     if(el.style && (v = el.style[prop])){
7747                         return v;
7748                     }
7749                     if(cs = view.getComputedStyle(el, "")){
7750                         if(!(camel = propCache[prop])){
7751                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7752                         }
7753                         return cs[camel];
7754                     }
7755                     return null;
7756                 } :
7757                 function(prop){
7758                     var el = this.dom, v, cs, camel;
7759                     if(prop == 'opacity'){
7760                         if(typeof el.style.filter == 'string'){
7761                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7762                             if(m){
7763                                 var fv = parseFloat(m[1]);
7764                                 if(!isNaN(fv)){
7765                                     return fv ? fv / 100 : 0;
7766                                 }
7767                             }
7768                         }
7769                         return 1;
7770                     }else if(prop == 'float'){
7771                         prop = "styleFloat";
7772                     }
7773                     if(!(camel = propCache[prop])){
7774                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7775                     }
7776                     if(v = el.style[camel]){
7777                         return v;
7778                     }
7779                     if(cs = el.currentStyle){
7780                         return cs[camel];
7781                     }
7782                     return null;
7783                 };
7784         }(),
7785
7786         /**
7787          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7788          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7789          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7790          * @return {Roo.Element} this
7791          */
7792         setStyle : function(prop, value){
7793             if(typeof prop == "string"){
7794                 
7795                 if (prop == 'float') {
7796                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7797                     return this;
7798                 }
7799                 
7800                 var camel;
7801                 if(!(camel = propCache[prop])){
7802                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7803                 }
7804                 
7805                 if(camel == 'opacity') {
7806                     this.setOpacity(value);
7807                 }else{
7808                     this.dom.style[camel] = value;
7809                 }
7810             }else{
7811                 for(var style in prop){
7812                     if(typeof prop[style] != "function"){
7813                        this.setStyle(style, prop[style]);
7814                     }
7815                 }
7816             }
7817             return this;
7818         },
7819
7820         /**
7821          * More flexible version of {@link #setStyle} for setting style properties.
7822          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7823          * a function which returns such a specification.
7824          * @return {Roo.Element} this
7825          */
7826         applyStyles : function(style){
7827             Roo.DomHelper.applyStyles(this.dom, style);
7828             return this;
7829         },
7830
7831         /**
7832           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7833           * @return {Number} The X position of the element
7834           */
7835         getX : function(){
7836             return D.getX(this.dom);
7837         },
7838
7839         /**
7840           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7841           * @return {Number} The Y position of the element
7842           */
7843         getY : function(){
7844             return D.getY(this.dom);
7845         },
7846
7847         /**
7848           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7849           * @return {Array} The XY position of the element
7850           */
7851         getXY : function(){
7852             return D.getXY(this.dom);
7853         },
7854
7855         /**
7856          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7857          * @param {Number} The X position of the element
7858          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7859          * @return {Roo.Element} this
7860          */
7861         setX : function(x, animate){
7862             if(!animate || !A){
7863                 D.setX(this.dom, x);
7864             }else{
7865                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7866             }
7867             return this;
7868         },
7869
7870         /**
7871          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7872          * @param {Number} The Y position of the element
7873          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7874          * @return {Roo.Element} this
7875          */
7876         setY : function(y, animate){
7877             if(!animate || !A){
7878                 D.setY(this.dom, y);
7879             }else{
7880                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7887          * @param {String} left The left CSS property value
7888          * @return {Roo.Element} this
7889          */
7890         setLeft : function(left){
7891             this.setStyle("left", this.addUnits(left));
7892             return this;
7893         },
7894
7895         /**
7896          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7897          * @param {String} top The top CSS property value
7898          * @return {Roo.Element} this
7899          */
7900         setTop : function(top){
7901             this.setStyle("top", this.addUnits(top));
7902             return this;
7903         },
7904
7905         /**
7906          * Sets the element's CSS right style.
7907          * @param {String} right The right CSS property value
7908          * @return {Roo.Element} this
7909          */
7910         setRight : function(right){
7911             this.setStyle("right", this.addUnits(right));
7912             return this;
7913         },
7914
7915         /**
7916          * Sets the element's CSS bottom style.
7917          * @param {String} bottom The bottom CSS property value
7918          * @return {Roo.Element} this
7919          */
7920         setBottom : function(bottom){
7921             this.setStyle("bottom", this.addUnits(bottom));
7922             return this;
7923         },
7924
7925         /**
7926          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7927          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7928          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7929          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7930          * @return {Roo.Element} this
7931          */
7932         setXY : function(pos, animate){
7933             if(!animate || !A){
7934                 D.setXY(this.dom, pos);
7935             }else{
7936                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7937             }
7938             return this;
7939         },
7940
7941         /**
7942          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7943          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7944          * @param {Number} x X value for new position (coordinates are page-based)
7945          * @param {Number} y Y value for new position (coordinates are page-based)
7946          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7947          * @return {Roo.Element} this
7948          */
7949         setLocation : function(x, y, animate){
7950             this.setXY([x, y], this.preanim(arguments, 2));
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7956          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7957          * @param {Number} x X value for new position (coordinates are page-based)
7958          * @param {Number} y Y value for new position (coordinates are page-based)
7959          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7960          * @return {Roo.Element} this
7961          */
7962         moveTo : function(x, y, animate){
7963             this.setXY([x, y], this.preanim(arguments, 2));
7964             return this;
7965         },
7966
7967         /**
7968          * Returns the region of the given element.
7969          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7970          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7971          */
7972         getRegion : function(){
7973             return D.getRegion(this.dom);
7974         },
7975
7976         /**
7977          * Returns the offset height of the element
7978          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7979          * @return {Number} The element's height
7980          */
7981         getHeight : function(contentHeight){
7982             var h = this.dom.offsetHeight || 0;
7983             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7984         },
7985
7986         /**
7987          * Returns the offset width of the element
7988          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7989          * @return {Number} The element's width
7990          */
7991         getWidth : function(contentWidth){
7992             var w = this.dom.offsetWidth || 0;
7993             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7994         },
7995
7996         /**
7997          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7998          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7999          * if a height has not been set using CSS.
8000          * @return {Number}
8001          */
8002         getComputedHeight : function(){
8003             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8004             if(!h){
8005                 h = parseInt(this.getStyle('height'), 10) || 0;
8006                 if(!this.isBorderBox()){
8007                     h += this.getFrameWidth('tb');
8008                 }
8009             }
8010             return h;
8011         },
8012
8013         /**
8014          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8015          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8016          * if a width has not been set using CSS.
8017          * @return {Number}
8018          */
8019         getComputedWidth : function(){
8020             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8021             if(!w){
8022                 w = parseInt(this.getStyle('width'), 10) || 0;
8023                 if(!this.isBorderBox()){
8024                     w += this.getFrameWidth('lr');
8025                 }
8026             }
8027             return w;
8028         },
8029
8030         /**
8031          * Returns the size of the element.
8032          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8033          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8034          */
8035         getSize : function(contentSize){
8036             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8037         },
8038
8039         /**
8040          * Returns the width and height of the viewport.
8041          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8042          */
8043         getViewSize : function(){
8044             var d = this.dom, doc = document, aw = 0, ah = 0;
8045             if(d == doc || d == doc.body){
8046                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8047             }else{
8048                 return {
8049                     width : d.clientWidth,
8050                     height: d.clientHeight
8051                 };
8052             }
8053         },
8054
8055         /**
8056          * Returns the value of the "value" attribute
8057          * @param {Boolean} asNumber true to parse the value as a number
8058          * @return {String/Number}
8059          */
8060         getValue : function(asNumber){
8061             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8062         },
8063
8064         // private
8065         adjustWidth : function(width){
8066             if(typeof width == "number"){
8067                 if(this.autoBoxAdjust && !this.isBorderBox()){
8068                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8069                 }
8070                 if(width < 0){
8071                     width = 0;
8072                 }
8073             }
8074             return width;
8075         },
8076
8077         // private
8078         adjustHeight : function(height){
8079             if(typeof height == "number"){
8080                if(this.autoBoxAdjust && !this.isBorderBox()){
8081                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8082                }
8083                if(height < 0){
8084                    height = 0;
8085                }
8086             }
8087             return height;
8088         },
8089
8090         /**
8091          * Set the width of the element
8092          * @param {Number} width The new width
8093          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8094          * @return {Roo.Element} this
8095          */
8096         setWidth : function(width, animate){
8097             width = this.adjustWidth(width);
8098             if(!animate || !A){
8099                 this.dom.style.width = this.addUnits(width);
8100             }else{
8101                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8102             }
8103             return this;
8104         },
8105
8106         /**
8107          * Set the height of the element
8108          * @param {Number} height The new height
8109          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8110          * @return {Roo.Element} this
8111          */
8112          setHeight : function(height, animate){
8113             height = this.adjustHeight(height);
8114             if(!animate || !A){
8115                 this.dom.style.height = this.addUnits(height);
8116             }else{
8117                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8118             }
8119             return this;
8120         },
8121
8122         /**
8123          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8124          * @param {Number} width The new width
8125          * @param {Number} height The new height
8126          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8127          * @return {Roo.Element} this
8128          */
8129          setSize : function(width, height, animate){
8130             if(typeof width == "object"){ // in case of object from getSize()
8131                 height = width.height; width = width.width;
8132             }
8133             width = this.adjustWidth(width); height = this.adjustHeight(height);
8134             if(!animate || !A){
8135                 this.dom.style.width = this.addUnits(width);
8136                 this.dom.style.height = this.addUnits(height);
8137             }else{
8138                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8139             }
8140             return this;
8141         },
8142
8143         /**
8144          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8145          * @param {Number} x X value for new position (coordinates are page-based)
8146          * @param {Number} y Y value for new position (coordinates are page-based)
8147          * @param {Number} width The new width
8148          * @param {Number} height The new height
8149          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8150          * @return {Roo.Element} this
8151          */
8152         setBounds : function(x, y, width, height, animate){
8153             if(!animate || !A){
8154                 this.setSize(width, height);
8155                 this.setLocation(x, y);
8156             }else{
8157                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8158                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8159                               this.preanim(arguments, 4), 'motion');
8160             }
8161             return this;
8162         },
8163
8164         /**
8165          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8166          * @param {Roo.lib.Region} region The region to fill
8167          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8168          * @return {Roo.Element} this
8169          */
8170         setRegion : function(region, animate){
8171             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8172             return this;
8173         },
8174
8175         /**
8176          * Appends an event handler
8177          *
8178          * @param {String}   eventName     The type of event to append
8179          * @param {Function} fn        The method the event invokes
8180          * @param {Object} scope       (optional) The scope (this object) of the fn
8181          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8182          */
8183         addListener : function(eventName, fn, scope, options){
8184             if (this.dom) {
8185                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8186             }
8187         },
8188
8189         /**
8190          * Removes an event handler from this element
8191          * @param {String} eventName the type of event to remove
8192          * @param {Function} fn the method the event invokes
8193          * @return {Roo.Element} this
8194          */
8195         removeListener : function(eventName, fn){
8196             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8197             return this;
8198         },
8199
8200         /**
8201          * Removes all previous added listeners from this element
8202          * @return {Roo.Element} this
8203          */
8204         removeAllListeners : function(){
8205             E.purgeElement(this.dom);
8206             return this;
8207         },
8208
8209         relayEvent : function(eventName, observable){
8210             this.on(eventName, function(e){
8211                 observable.fireEvent(eventName, e);
8212             });
8213         },
8214
8215         /**
8216          * Set the opacity of the element
8217          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8218          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8219          * @return {Roo.Element} this
8220          */
8221          setOpacity : function(opacity, animate){
8222             if(!animate || !A){
8223                 var s = this.dom.style;
8224                 if(Roo.isIE){
8225                     s.zoom = 1;
8226                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8227                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8228                 }else{
8229                     s.opacity = opacity;
8230                 }
8231             }else{
8232                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8233             }
8234             return this;
8235         },
8236
8237         /**
8238          * Gets the left X coordinate
8239          * @param {Boolean} local True to get the local css position instead of page coordinate
8240          * @return {Number}
8241          */
8242         getLeft : function(local){
8243             if(!local){
8244                 return this.getX();
8245             }else{
8246                 return parseInt(this.getStyle("left"), 10) || 0;
8247             }
8248         },
8249
8250         /**
8251          * Gets the right X coordinate of the element (element X position + element width)
8252          * @param {Boolean} local True to get the local css position instead of page coordinate
8253          * @return {Number}
8254          */
8255         getRight : function(local){
8256             if(!local){
8257                 return this.getX() + this.getWidth();
8258             }else{
8259                 return (this.getLeft(true) + this.getWidth()) || 0;
8260             }
8261         },
8262
8263         /**
8264          * Gets the top Y coordinate
8265          * @param {Boolean} local True to get the local css position instead of page coordinate
8266          * @return {Number}
8267          */
8268         getTop : function(local) {
8269             if(!local){
8270                 return this.getY();
8271             }else{
8272                 return parseInt(this.getStyle("top"), 10) || 0;
8273             }
8274         },
8275
8276         /**
8277          * Gets the bottom Y coordinate of the element (element Y position + element height)
8278          * @param {Boolean} local True to get the local css position instead of page coordinate
8279          * @return {Number}
8280          */
8281         getBottom : function(local){
8282             if(!local){
8283                 return this.getY() + this.getHeight();
8284             }else{
8285                 return (this.getTop(true) + this.getHeight()) || 0;
8286             }
8287         },
8288
8289         /**
8290         * Initializes positioning on this element. If a desired position is not passed, it will make the
8291         * the element positioned relative IF it is not already positioned.
8292         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8293         * @param {Number} zIndex (optional) The zIndex to apply
8294         * @param {Number} x (optional) Set the page X position
8295         * @param {Number} y (optional) Set the page Y position
8296         */
8297         position : function(pos, zIndex, x, y){
8298             if(!pos){
8299                if(this.getStyle('position') == 'static'){
8300                    this.setStyle('position', 'relative');
8301                }
8302             }else{
8303                 this.setStyle("position", pos);
8304             }
8305             if(zIndex){
8306                 this.setStyle("z-index", zIndex);
8307             }
8308             if(x !== undefined && y !== undefined){
8309                 this.setXY([x, y]);
8310             }else if(x !== undefined){
8311                 this.setX(x);
8312             }else if(y !== undefined){
8313                 this.setY(y);
8314             }
8315         },
8316
8317         /**
8318         * Clear positioning back to the default when the document was loaded
8319         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8320         * @return {Roo.Element} this
8321          */
8322         clearPositioning : function(value){
8323             value = value ||'';
8324             this.setStyle({
8325                 "left": value,
8326                 "right": value,
8327                 "top": value,
8328                 "bottom": value,
8329                 "z-index": "",
8330                 "position" : "static"
8331             });
8332             return this;
8333         },
8334
8335         /**
8336         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8337         * snapshot before performing an update and then restoring the element.
8338         * @return {Object}
8339         */
8340         getPositioning : function(){
8341             var l = this.getStyle("left");
8342             var t = this.getStyle("top");
8343             return {
8344                 "position" : this.getStyle("position"),
8345                 "left" : l,
8346                 "right" : l ? "" : this.getStyle("right"),
8347                 "top" : t,
8348                 "bottom" : t ? "" : this.getStyle("bottom"),
8349                 "z-index" : this.getStyle("z-index")
8350             };
8351         },
8352
8353         /**
8354          * Gets the width of the border(s) for the specified side(s)
8355          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8356          * passing lr would get the border (l)eft width + the border (r)ight width.
8357          * @return {Number} The width of the sides passed added together
8358          */
8359         getBorderWidth : function(side){
8360             return this.addStyles(side, El.borders);
8361         },
8362
8363         /**
8364          * Gets the width of the padding(s) for the specified side(s)
8365          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8366          * passing lr would get the padding (l)eft + the padding (r)ight.
8367          * @return {Number} The padding of the sides passed added together
8368          */
8369         getPadding : function(side){
8370             return this.addStyles(side, El.paddings);
8371         },
8372
8373         /**
8374         * Set positioning with an object returned by getPositioning().
8375         * @param {Object} posCfg
8376         * @return {Roo.Element} this
8377          */
8378         setPositioning : function(pc){
8379             this.applyStyles(pc);
8380             if(pc.right == "auto"){
8381                 this.dom.style.right = "";
8382             }
8383             if(pc.bottom == "auto"){
8384                 this.dom.style.bottom = "";
8385             }
8386             return this;
8387         },
8388
8389         // private
8390         fixDisplay : function(){
8391             if(this.getStyle("display") == "none"){
8392                 this.setStyle("visibility", "hidden");
8393                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8394                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8395                     this.setStyle("display", "block");
8396                 }
8397             }
8398         },
8399
8400         /**
8401          * Quick set left and top adding default units
8402          * @param {String} left The left CSS property value
8403          * @param {String} top The top CSS property value
8404          * @return {Roo.Element} this
8405          */
8406          setLeftTop : function(left, top){
8407             this.dom.style.left = this.addUnits(left);
8408             this.dom.style.top = this.addUnits(top);
8409             return this;
8410         },
8411
8412         /**
8413          * Move this element relative to its current position.
8414          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8415          * @param {Number} distance How far to move the element in pixels
8416          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8417          * @return {Roo.Element} this
8418          */
8419          move : function(direction, distance, animate){
8420             var xy = this.getXY();
8421             direction = direction.toLowerCase();
8422             switch(direction){
8423                 case "l":
8424                 case "left":
8425                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8426                     break;
8427                case "r":
8428                case "right":
8429                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8430                     break;
8431                case "t":
8432                case "top":
8433                case "up":
8434                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8435                     break;
8436                case "b":
8437                case "bottom":
8438                case "down":
8439                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8440                     break;
8441             }
8442             return this;
8443         },
8444
8445         /**
8446          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8447          * @return {Roo.Element} this
8448          */
8449         clip : function(){
8450             if(!this.isClipped){
8451                this.isClipped = true;
8452                this.originalClip = {
8453                    "o": this.getStyle("overflow"),
8454                    "x": this.getStyle("overflow-x"),
8455                    "y": this.getStyle("overflow-y")
8456                };
8457                this.setStyle("overflow", "hidden");
8458                this.setStyle("overflow-x", "hidden");
8459                this.setStyle("overflow-y", "hidden");
8460             }
8461             return this;
8462         },
8463
8464         /**
8465          *  Return clipping (overflow) to original clipping before clip() was called
8466          * @return {Roo.Element} this
8467          */
8468         unclip : function(){
8469             if(this.isClipped){
8470                 this.isClipped = false;
8471                 var o = this.originalClip;
8472                 if(o.o){this.setStyle("overflow", o.o);}
8473                 if(o.x){this.setStyle("overflow-x", o.x);}
8474                 if(o.y){this.setStyle("overflow-y", o.y);}
8475             }
8476             return this;
8477         },
8478
8479
8480         /**
8481          * Gets the x,y coordinates specified by the anchor position on the element.
8482          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8483          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8484          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8485          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8486          * @return {Array} [x, y] An array containing the element's x and y coordinates
8487          */
8488         getAnchorXY : function(anchor, local, s){
8489             //Passing a different size is useful for pre-calculating anchors,
8490             //especially for anchored animations that change the el size.
8491
8492             var w, h, vp = false;
8493             if(!s){
8494                 var d = this.dom;
8495                 if(d == document.body || d == document){
8496                     vp = true;
8497                     w = D.getViewWidth(); h = D.getViewHeight();
8498                 }else{
8499                     w = this.getWidth(); h = this.getHeight();
8500                 }
8501             }else{
8502                 w = s.width;  h = s.height;
8503             }
8504             var x = 0, y = 0, r = Math.round;
8505             switch((anchor || "tl").toLowerCase()){
8506                 case "c":
8507                     x = r(w*.5);
8508                     y = r(h*.5);
8509                 break;
8510                 case "t":
8511                     x = r(w*.5);
8512                     y = 0;
8513                 break;
8514                 case "l":
8515                     x = 0;
8516                     y = r(h*.5);
8517                 break;
8518                 case "r":
8519                     x = w;
8520                     y = r(h*.5);
8521                 break;
8522                 case "b":
8523                     x = r(w*.5);
8524                     y = h;
8525                 break;
8526                 case "tl":
8527                     x = 0;
8528                     y = 0;
8529                 break;
8530                 case "bl":
8531                     x = 0;
8532                     y = h;
8533                 break;
8534                 case "br":
8535                     x = w;
8536                     y = h;
8537                 break;
8538                 case "tr":
8539                     x = w;
8540                     y = 0;
8541                 break;
8542             }
8543             if(local === true){
8544                 return [x, y];
8545             }
8546             if(vp){
8547                 var sc = this.getScroll();
8548                 return [x + sc.left, y + sc.top];
8549             }
8550             //Add the element's offset xy
8551             var o = this.getXY();
8552             return [x+o[0], y+o[1]];
8553         },
8554
8555         /**
8556          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8557          * supported position values.
8558          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8559          * @param {String} position The position to align to.
8560          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8561          * @return {Array} [x, y]
8562          */
8563         getAlignToXY : function(el, p, o){
8564             el = Roo.get(el);
8565             var d = this.dom;
8566             if(!el.dom){
8567                 throw "Element.alignTo with an element that doesn't exist";
8568             }
8569             var c = false; //constrain to viewport
8570             var p1 = "", p2 = "";
8571             o = o || [0,0];
8572
8573             if(!p){
8574                 p = "tl-bl";
8575             }else if(p == "?"){
8576                 p = "tl-bl?";
8577             }else if(p.indexOf("-") == -1){
8578                 p = "tl-" + p;
8579             }
8580             p = p.toLowerCase();
8581             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8582             if(!m){
8583                throw "Element.alignTo with an invalid alignment " + p;
8584             }
8585             p1 = m[1]; p2 = m[2]; c = !!m[3];
8586
8587             //Subtract the aligned el's internal xy from the target's offset xy
8588             //plus custom offset to get the aligned el's new offset xy
8589             var a1 = this.getAnchorXY(p1, true);
8590             var a2 = el.getAnchorXY(p2, false);
8591             var x = a2[0] - a1[0] + o[0];
8592             var y = a2[1] - a1[1] + o[1];
8593             if(c){
8594                 //constrain the aligned el to viewport if necessary
8595                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8596                 // 5px of margin for ie
8597                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8598
8599                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8600                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8601                 //otherwise swap the aligned el to the opposite border of the target.
8602                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8603                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8604                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8605                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8606
8607                var doc = document;
8608                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8609                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8610
8611                if((x+w) > dw + scrollX){
8612                     x = swapX ? r.left-w : dw+scrollX-w;
8613                 }
8614                if(x < scrollX){
8615                    x = swapX ? r.right : scrollX;
8616                }
8617                if((y+h) > dh + scrollY){
8618                     y = swapY ? r.top-h : dh+scrollY-h;
8619                 }
8620                if (y < scrollY){
8621                    y = swapY ? r.bottom : scrollY;
8622                }
8623             }
8624             return [x,y];
8625         },
8626
8627         // private
8628         getConstrainToXY : function(){
8629             var os = {top:0, left:0, bottom:0, right: 0};
8630
8631             return function(el, local, offsets, proposedXY){
8632                 el = Roo.get(el);
8633                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8634
8635                 var vw, vh, vx = 0, vy = 0;
8636                 if(el.dom == document.body || el.dom == document){
8637                     vw = Roo.lib.Dom.getViewWidth();
8638                     vh = Roo.lib.Dom.getViewHeight();
8639                 }else{
8640                     vw = el.dom.clientWidth;
8641                     vh = el.dom.clientHeight;
8642                     if(!local){
8643                         var vxy = el.getXY();
8644                         vx = vxy[0];
8645                         vy = vxy[1];
8646                     }
8647                 }
8648
8649                 var s = el.getScroll();
8650
8651                 vx += offsets.left + s.left;
8652                 vy += offsets.top + s.top;
8653
8654                 vw -= offsets.right;
8655                 vh -= offsets.bottom;
8656
8657                 var vr = vx+vw;
8658                 var vb = vy+vh;
8659
8660                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8661                 var x = xy[0], y = xy[1];
8662                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8663
8664                 // only move it if it needs it
8665                 var moved = false;
8666
8667                 // first validate right/bottom
8668                 if((x + w) > vr){
8669                     x = vr - w;
8670                     moved = true;
8671                 }
8672                 if((y + h) > vb){
8673                     y = vb - h;
8674                     moved = true;
8675                 }
8676                 // then make sure top/left isn't negative
8677                 if(x < vx){
8678                     x = vx;
8679                     moved = true;
8680                 }
8681                 if(y < vy){
8682                     y = vy;
8683                     moved = true;
8684                 }
8685                 return moved ? [x, y] : false;
8686             };
8687         }(),
8688
8689         // private
8690         adjustForConstraints : function(xy, parent, offsets){
8691             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8692         },
8693
8694         /**
8695          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8696          * document it aligns it to the viewport.
8697          * The position parameter is optional, and can be specified in any one of the following formats:
8698          * <ul>
8699          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8700          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8701          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8702          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8703          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8704          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8705          * </ul>
8706          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8707          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8708          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8709          * that specified in order to enforce the viewport constraints.
8710          * Following are all of the supported anchor positions:
8711     <pre>
8712     Value  Description
8713     -----  -----------------------------
8714     tl     The top left corner (default)
8715     t      The center of the top edge
8716     tr     The top right corner
8717     l      The center of the left edge
8718     c      In the center of the element
8719     r      The center of the right edge
8720     bl     The bottom left corner
8721     b      The center of the bottom edge
8722     br     The bottom right corner
8723     </pre>
8724     Example Usage:
8725     <pre><code>
8726     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8727     el.alignTo("other-el");
8728
8729     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8730     el.alignTo("other-el", "tr?");
8731
8732     // align the bottom right corner of el with the center left edge of other-el
8733     el.alignTo("other-el", "br-l?");
8734
8735     // align the center of el with the bottom left corner of other-el and
8736     // adjust the x position by -6 pixels (and the y position by 0)
8737     el.alignTo("other-el", "c-bl", [-6, 0]);
8738     </code></pre>
8739          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8740          * @param {String} position The position to align to.
8741          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8742          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8743          * @return {Roo.Element} this
8744          */
8745         alignTo : function(element, position, offsets, animate){
8746             var xy = this.getAlignToXY(element, position, offsets);
8747             this.setXY(xy, this.preanim(arguments, 3));
8748             return this;
8749         },
8750
8751         /**
8752          * Anchors an element to another element and realigns it when the window is resized.
8753          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8754          * @param {String} position The position to align to.
8755          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8756          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8757          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8758          * is a number, it is used as the buffer delay (defaults to 50ms).
8759          * @param {Function} callback The function to call after the animation finishes
8760          * @return {Roo.Element} this
8761          */
8762         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8763             var action = function(){
8764                 this.alignTo(el, alignment, offsets, animate);
8765                 Roo.callback(callback, this);
8766             };
8767             Roo.EventManager.onWindowResize(action, this);
8768             var tm = typeof monitorScroll;
8769             if(tm != 'undefined'){
8770                 Roo.EventManager.on(window, 'scroll', action, this,
8771                     {buffer: tm == 'number' ? monitorScroll : 50});
8772             }
8773             action.call(this); // align immediately
8774             return this;
8775         },
8776         /**
8777          * Clears any opacity settings from this element. Required in some cases for IE.
8778          * @return {Roo.Element} this
8779          */
8780         clearOpacity : function(){
8781             if (window.ActiveXObject) {
8782                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8783                     this.dom.style.filter = "";
8784                 }
8785             } else {
8786                 this.dom.style.opacity = "";
8787                 this.dom.style["-moz-opacity"] = "";
8788                 this.dom.style["-khtml-opacity"] = "";
8789             }
8790             return this;
8791         },
8792
8793         /**
8794          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8795          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8796          * @return {Roo.Element} this
8797          */
8798         hide : function(animate){
8799             this.setVisible(false, this.preanim(arguments, 0));
8800             return this;
8801         },
8802
8803         /**
8804         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8805         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8806          * @return {Roo.Element} this
8807          */
8808         show : function(animate){
8809             this.setVisible(true, this.preanim(arguments, 0));
8810             return this;
8811         },
8812
8813         /**
8814          * @private Test if size has a unit, otherwise appends the default
8815          */
8816         addUnits : function(size){
8817             return Roo.Element.addUnits(size, this.defaultUnit);
8818         },
8819
8820         /**
8821          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8822          * @return {Roo.Element} this
8823          */
8824         beginMeasure : function(){
8825             var el = this.dom;
8826             if(el.offsetWidth || el.offsetHeight){
8827                 return this; // offsets work already
8828             }
8829             var changed = [];
8830             var p = this.dom, b = document.body; // start with this element
8831             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8832                 var pe = Roo.get(p);
8833                 if(pe.getStyle('display') == 'none'){
8834                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8835                     p.style.visibility = "hidden";
8836                     p.style.display = "block";
8837                 }
8838                 p = p.parentNode;
8839             }
8840             this._measureChanged = changed;
8841             return this;
8842
8843         },
8844
8845         /**
8846          * Restores displays to before beginMeasure was called
8847          * @return {Roo.Element} this
8848          */
8849         endMeasure : function(){
8850             var changed = this._measureChanged;
8851             if(changed){
8852                 for(var i = 0, len = changed.length; i < len; i++) {
8853                     var r = changed[i];
8854                     r.el.style.visibility = r.visibility;
8855                     r.el.style.display = "none";
8856                 }
8857                 this._measureChanged = null;
8858             }
8859             return this;
8860         },
8861
8862         /**
8863         * Update the innerHTML of this element, optionally searching for and processing scripts
8864         * @param {String} html The new HTML
8865         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8866         * @param {Function} callback For async script loading you can be noticed when the update completes
8867         * @return {Roo.Element} this
8868          */
8869         update : function(html, loadScripts, callback){
8870             if(typeof html == "undefined"){
8871                 html = "";
8872             }
8873             if(loadScripts !== true){
8874                 this.dom.innerHTML = html;
8875                 if(typeof callback == "function"){
8876                     callback();
8877                 }
8878                 return this;
8879             }
8880             var id = Roo.id();
8881             var dom = this.dom;
8882
8883             html += '<span id="' + id + '"></span>';
8884
8885             E.onAvailable(id, function(){
8886                 var hd = document.getElementsByTagName("head")[0];
8887                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8888                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8889                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8890
8891                 var match;
8892                 while(match = re.exec(html)){
8893                     var attrs = match[1];
8894                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8895                     if(srcMatch && srcMatch[2]){
8896                        var s = document.createElement("script");
8897                        s.src = srcMatch[2];
8898                        var typeMatch = attrs.match(typeRe);
8899                        if(typeMatch && typeMatch[2]){
8900                            s.type = typeMatch[2];
8901                        }
8902                        hd.appendChild(s);
8903                     }else if(match[2] && match[2].length > 0){
8904                         if(window.execScript) {
8905                            window.execScript(match[2]);
8906                         } else {
8907                             /**
8908                              * eval:var:id
8909                              * eval:var:dom
8910                              * eval:var:html
8911                              * 
8912                              */
8913                            window.eval(match[2]);
8914                         }
8915                     }
8916                 }
8917                 var el = document.getElementById(id);
8918                 if(el){el.parentNode.removeChild(el);}
8919                 if(typeof callback == "function"){
8920                     callback();
8921                 }
8922             });
8923             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8924             return this;
8925         },
8926
8927         /**
8928          * Direct access to the UpdateManager update() method (takes the same parameters).
8929          * @param {String/Function} url The url for this request or a function to call to get the url
8930          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8931          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8932          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8933          * @return {Roo.Element} this
8934          */
8935         load : function(){
8936             var um = this.getUpdateManager();
8937             um.update.apply(um, arguments);
8938             return this;
8939         },
8940
8941         /**
8942         * Gets this element's UpdateManager
8943         * @return {Roo.UpdateManager} The UpdateManager
8944         */
8945         getUpdateManager : function(){
8946             if(!this.updateManager){
8947                 this.updateManager = new Roo.UpdateManager(this);
8948             }
8949             return this.updateManager;
8950         },
8951
8952         /**
8953          * Disables text selection for this element (normalized across browsers)
8954          * @return {Roo.Element} this
8955          */
8956         unselectable : function(){
8957             this.dom.unselectable = "on";
8958             this.swallowEvent("selectstart", true);
8959             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8960             this.addClass("x-unselectable");
8961             return this;
8962         },
8963
8964         /**
8965         * Calculates the x, y to center this element on the screen
8966         * @return {Array} The x, y values [x, y]
8967         */
8968         getCenterXY : function(){
8969             return this.getAlignToXY(document, 'c-c');
8970         },
8971
8972         /**
8973         * Centers the Element in either the viewport, or another Element.
8974         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8975         */
8976         center : function(centerIn){
8977             this.alignTo(centerIn || document, 'c-c');
8978             return this;
8979         },
8980
8981         /**
8982          * Tests various css rules/browsers to determine if this element uses a border box
8983          * @return {Boolean}
8984          */
8985         isBorderBox : function(){
8986             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8987         },
8988
8989         /**
8990          * Return a box {x, y, width, height} that can be used to set another elements
8991          * size/location to match this element.
8992          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8993          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8994          * @return {Object} box An object in the format {x, y, width, height}
8995          */
8996         getBox : function(contentBox, local){
8997             var xy;
8998             if(!local){
8999                 xy = this.getXY();
9000             }else{
9001                 var left = parseInt(this.getStyle("left"), 10) || 0;
9002                 var top = parseInt(this.getStyle("top"), 10) || 0;
9003                 xy = [left, top];
9004             }
9005             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9006             if(!contentBox){
9007                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9008             }else{
9009                 var l = this.getBorderWidth("l")+this.getPadding("l");
9010                 var r = this.getBorderWidth("r")+this.getPadding("r");
9011                 var t = this.getBorderWidth("t")+this.getPadding("t");
9012                 var b = this.getBorderWidth("b")+this.getPadding("b");
9013                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
9014             }
9015             bx.right = bx.x + bx.width;
9016             bx.bottom = bx.y + bx.height;
9017             return bx;
9018         },
9019
9020         /**
9021          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9022          for more information about the sides.
9023          * @param {String} sides
9024          * @return {Number}
9025          */
9026         getFrameWidth : function(sides, onlyContentBox){
9027             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9028         },
9029
9030         /**
9031          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
9032          * @param {Object} box The box to fill {x, y, width, height}
9033          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9034          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9035          * @return {Roo.Element} this
9036          */
9037         setBox : function(box, adjust, animate){
9038             var w = box.width, h = box.height;
9039             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9040                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9041                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9042             }
9043             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9044             return this;
9045         },
9046
9047         /**
9048          * Forces the browser to repaint this element
9049          * @return {Roo.Element} this
9050          */
9051          repaint : function(){
9052             var dom = this.dom;
9053             this.addClass("x-repaint");
9054             setTimeout(function(){
9055                 Roo.get(dom).removeClass("x-repaint");
9056             }, 1);
9057             return this;
9058         },
9059
9060         /**
9061          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9062          * then it returns the calculated width of the sides (see getPadding)
9063          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9064          * @return {Object/Number}
9065          */
9066         getMargins : function(side){
9067             if(!side){
9068                 return {
9069                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9070                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9071                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9072                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9073                 };
9074             }else{
9075                 return this.addStyles(side, El.margins);
9076              }
9077         },
9078
9079         // private
9080         addStyles : function(sides, styles){
9081             var val = 0, v, w;
9082             for(var i = 0, len = sides.length; i < len; i++){
9083                 v = this.getStyle(styles[sides.charAt(i)]);
9084                 if(v){
9085                      w = parseInt(v, 10);
9086                      if(w){ val += w; }
9087                 }
9088             }
9089             return val;
9090         },
9091
9092         /**
9093          * Creates a proxy element of this element
9094          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9095          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9096          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9097          * @return {Roo.Element} The new proxy element
9098          */
9099         createProxy : function(config, renderTo, matchBox){
9100             if(renderTo){
9101                 renderTo = Roo.getDom(renderTo);
9102             }else{
9103                 renderTo = document.body;
9104             }
9105             config = typeof config == "object" ?
9106                 config : {tag : "div", cls: config};
9107             var proxy = Roo.DomHelper.append(renderTo, config, true);
9108             if(matchBox){
9109                proxy.setBox(this.getBox());
9110             }
9111             return proxy;
9112         },
9113
9114         /**
9115          * Puts a mask over this element to disable user interaction. Requires core.css.
9116          * This method can only be applied to elements which accept child nodes.
9117          * @param {String} msg (optional) A message to display in the mask
9118          * @param {String} msgCls (optional) A css class to apply to the msg element
9119          * @return {Element} The mask  element
9120          */
9121         mask : function(msg, msgCls)
9122         {
9123             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9124                 this.setStyle("position", "relative");
9125             }
9126             if(!this._mask){
9127                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9128             }
9129             
9130             this.addClass("x-masked");
9131             this._mask.setDisplayed(true);
9132             
9133             // we wander
9134             var z = 0;
9135             var dom = this.dom;
9136             while (dom && dom.style) {
9137                 if (!isNaN(parseInt(dom.style.zIndex))) {
9138                     z = Math.max(z, parseInt(dom.style.zIndex));
9139                 }
9140                 dom = dom.parentNode;
9141             }
9142             // if we are masking the body - then it hides everything..
9143             if (this.dom == document.body) {
9144                 z = 1000000;
9145                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9146                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9147             }
9148            
9149             if(typeof msg == 'string'){
9150                 if(!this._maskMsg){
9151                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9152                         cls: "roo-el-mask-msg", 
9153                         cn: [
9154                             {
9155                                 tag: 'i',
9156                                 cls: 'fa fa-spinner fa-spin'
9157                             },
9158                             {
9159                                 tag: 'div'
9160                             }   
9161                         ]
9162                     }, true);
9163                 }
9164                 var mm = this._maskMsg;
9165                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9166                 if (mm.dom.lastChild) { // weird IE issue?
9167                     mm.dom.lastChild.innerHTML = msg;
9168                 }
9169                 mm.setDisplayed(true);
9170                 mm.center(this);
9171                 mm.setStyle('z-index', z + 102);
9172             }
9173             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9174                 this._mask.setHeight(this.getHeight());
9175             }
9176             this._mask.setStyle('z-index', z + 100);
9177             
9178             return this._mask;
9179         },
9180
9181         /**
9182          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9183          * it is cached for reuse.
9184          */
9185         unmask : function(removeEl){
9186             if(this._mask){
9187                 if(removeEl === true){
9188                     this._mask.remove();
9189                     delete this._mask;
9190                     if(this._maskMsg){
9191                         this._maskMsg.remove();
9192                         delete this._maskMsg;
9193                     }
9194                 }else{
9195                     this._mask.setDisplayed(false);
9196                     if(this._maskMsg){
9197                         this._maskMsg.setDisplayed(false);
9198                     }
9199                 }
9200             }
9201             this.removeClass("x-masked");
9202         },
9203
9204         /**
9205          * Returns true if this element is masked
9206          * @return {Boolean}
9207          */
9208         isMasked : function(){
9209             return this._mask && this._mask.isVisible();
9210         },
9211
9212         /**
9213          * Creates an iframe shim for this element to keep selects and other windowed objects from
9214          * showing through.
9215          * @return {Roo.Element} The new shim element
9216          */
9217         createShim : function(){
9218             var el = document.createElement('iframe');
9219             el.frameBorder = 'no';
9220             el.className = 'roo-shim';
9221             if(Roo.isIE && Roo.isSecure){
9222                 el.src = Roo.SSL_SECURE_URL;
9223             }
9224             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9225             shim.autoBoxAdjust = false;
9226             return shim;
9227         },
9228
9229         /**
9230          * Removes this element from the DOM and deletes it from the cache
9231          */
9232         remove : function(){
9233             if(this.dom.parentNode){
9234                 this.dom.parentNode.removeChild(this.dom);
9235             }
9236             delete El.cache[this.dom.id];
9237         },
9238
9239         /**
9240          * Sets up event handlers to add and remove a css class when the mouse is over this element
9241          * @param {String} className
9242          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9243          * mouseout events for children elements
9244          * @return {Roo.Element} this
9245          */
9246         addClassOnOver : function(className, preventFlicker){
9247             this.on("mouseover", function(){
9248                 Roo.fly(this, '_internal').addClass(className);
9249             }, this.dom);
9250             var removeFn = function(e){
9251                 if(preventFlicker !== true || !e.within(this, true)){
9252                     Roo.fly(this, '_internal').removeClass(className);
9253                 }
9254             };
9255             this.on("mouseout", removeFn, this.dom);
9256             return this;
9257         },
9258
9259         /**
9260          * Sets up event handlers to add and remove a css class when this element has the focus
9261          * @param {String} className
9262          * @return {Roo.Element} this
9263          */
9264         addClassOnFocus : function(className){
9265             this.on("focus", function(){
9266                 Roo.fly(this, '_internal').addClass(className);
9267             }, this.dom);
9268             this.on("blur", function(){
9269                 Roo.fly(this, '_internal').removeClass(className);
9270             }, this.dom);
9271             return this;
9272         },
9273         /**
9274          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9275          * @param {String} className
9276          * @return {Roo.Element} this
9277          */
9278         addClassOnClick : function(className){
9279             var dom = this.dom;
9280             this.on("mousedown", function(){
9281                 Roo.fly(dom, '_internal').addClass(className);
9282                 var d = Roo.get(document);
9283                 var fn = function(){
9284                     Roo.fly(dom, '_internal').removeClass(className);
9285                     d.removeListener("mouseup", fn);
9286                 };
9287                 d.on("mouseup", fn);
9288             });
9289             return this;
9290         },
9291
9292         /**
9293          * Stops the specified event from bubbling and optionally prevents the default action
9294          * @param {String} eventName
9295          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9296          * @return {Roo.Element} this
9297          */
9298         swallowEvent : function(eventName, preventDefault){
9299             var fn = function(e){
9300                 e.stopPropagation();
9301                 if(preventDefault){
9302                     e.preventDefault();
9303                 }
9304             };
9305             if(eventName instanceof Array){
9306                 for(var i = 0, len = eventName.length; i < len; i++){
9307                      this.on(eventName[i], fn);
9308                 }
9309                 return this;
9310             }
9311             this.on(eventName, fn);
9312             return this;
9313         },
9314
9315         /**
9316          * @private
9317          */
9318       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9319
9320         /**
9321          * Sizes this element to its parent element's dimensions performing
9322          * neccessary box adjustments.
9323          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9324          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9325          * @return {Roo.Element} this
9326          */
9327         fitToParent : function(monitorResize, targetParent) {
9328           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9329           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9330           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9331             return;
9332           }
9333           var p = Roo.get(targetParent || this.dom.parentNode);
9334           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9335           if (monitorResize === true) {
9336             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9337             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9338           }
9339           return this;
9340         },
9341
9342         /**
9343          * Gets the next sibling, skipping text nodes
9344          * @return {HTMLElement} The next sibling or null
9345          */
9346         getNextSibling : function(){
9347             var n = this.dom.nextSibling;
9348             while(n && n.nodeType != 1){
9349                 n = n.nextSibling;
9350             }
9351             return n;
9352         },
9353
9354         /**
9355          * Gets the previous sibling, skipping text nodes
9356          * @return {HTMLElement} The previous sibling or null
9357          */
9358         getPrevSibling : function(){
9359             var n = this.dom.previousSibling;
9360             while(n && n.nodeType != 1){
9361                 n = n.previousSibling;
9362             }
9363             return n;
9364         },
9365
9366
9367         /**
9368          * Appends the passed element(s) to this element
9369          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9370          * @return {Roo.Element} this
9371          */
9372         appendChild: function(el){
9373             el = Roo.get(el);
9374             el.appendTo(this);
9375             return this;
9376         },
9377
9378         /**
9379          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9380          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9381          * automatically generated with the specified attributes.
9382          * @param {HTMLElement} insertBefore (optional) a child element of this element
9383          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9384          * @return {Roo.Element} The new child element
9385          */
9386         createChild: function(config, insertBefore, returnDom){
9387             config = config || {tag:'div'};
9388             if(insertBefore){
9389                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9390             }
9391             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9392         },
9393
9394         /**
9395          * Appends this element to the passed element
9396          * @param {String/HTMLElement/Element} el The new parent element
9397          * @return {Roo.Element} this
9398          */
9399         appendTo: function(el){
9400             el = Roo.getDom(el);
9401             el.appendChild(this.dom);
9402             return this;
9403         },
9404
9405         /**
9406          * Inserts this element before the passed element in the DOM
9407          * @param {String/HTMLElement/Element} el The element to insert before
9408          * @return {Roo.Element} this
9409          */
9410         insertBefore: function(el){
9411             el = Roo.getDom(el);
9412             el.parentNode.insertBefore(this.dom, el);
9413             return this;
9414         },
9415
9416         /**
9417          * Inserts this element after the passed element in the DOM
9418          * @param {String/HTMLElement/Element} el The element to insert after
9419          * @return {Roo.Element} this
9420          */
9421         insertAfter: function(el){
9422             el = Roo.getDom(el);
9423             el.parentNode.insertBefore(this.dom, el.nextSibling);
9424             return this;
9425         },
9426
9427         /**
9428          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9429          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9430          * @return {Roo.Element} The new child
9431          */
9432         insertFirst: function(el, returnDom){
9433             el = el || {};
9434             if(typeof el == 'object' && !el.nodeType){ // dh config
9435                 return this.createChild(el, this.dom.firstChild, returnDom);
9436             }else{
9437                 el = Roo.getDom(el);
9438                 this.dom.insertBefore(el, this.dom.firstChild);
9439                 return !returnDom ? Roo.get(el) : el;
9440             }
9441         },
9442
9443         /**
9444          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9445          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9446          * @param {String} where (optional) 'before' or 'after' defaults to before
9447          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9448          * @return {Roo.Element} the inserted Element
9449          */
9450         insertSibling: function(el, where, returnDom){
9451             where = where ? where.toLowerCase() : 'before';
9452             el = el || {};
9453             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9454
9455             if(typeof el == 'object' && !el.nodeType){ // dh config
9456                 if(where == 'after' && !this.dom.nextSibling){
9457                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9458                 }else{
9459                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9460                 }
9461
9462             }else{
9463                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9464                             where == 'before' ? this.dom : this.dom.nextSibling);
9465                 if(!returnDom){
9466                     rt = Roo.get(rt);
9467                 }
9468             }
9469             return rt;
9470         },
9471
9472         /**
9473          * Creates and wraps this element with another element
9474          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9475          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9476          * @return {HTMLElement/Element} The newly created wrapper element
9477          */
9478         wrap: function(config, returnDom){
9479             if(!config){
9480                 config = {tag: "div"};
9481             }
9482             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9483             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9484             return newEl;
9485         },
9486
9487         /**
9488          * Replaces the passed element with this element
9489          * @param {String/HTMLElement/Element} el The element to replace
9490          * @return {Roo.Element} this
9491          */
9492         replace: function(el){
9493             el = Roo.get(el);
9494             this.insertBefore(el);
9495             el.remove();
9496             return this;
9497         },
9498
9499         /**
9500          * Inserts an html fragment into this element
9501          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9502          * @param {String} html The HTML fragment
9503          * @param {Boolean} returnEl True to return an Roo.Element
9504          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9505          */
9506         insertHtml : function(where, html, returnEl){
9507             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9508             return returnEl ? Roo.get(el) : el;
9509         },
9510
9511         /**
9512          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9513          * @param {Object} o The object with the attributes
9514          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9515          * @return {Roo.Element} this
9516          */
9517         set : function(o, useSet){
9518             var el = this.dom;
9519             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9520             for(var attr in o){
9521                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9522                 if(attr=="cls"){
9523                     el.className = o["cls"];
9524                 }else{
9525                     if(useSet) {
9526                         el.setAttribute(attr, o[attr]);
9527                     } else {
9528                         el[attr] = o[attr];
9529                     }
9530                 }
9531             }
9532             if(o.style){
9533                 Roo.DomHelper.applyStyles(el, o.style);
9534             }
9535             return this;
9536         },
9537
9538         /**
9539          * Convenience method for constructing a KeyMap
9540          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9541          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9542          * @param {Function} fn The function to call
9543          * @param {Object} scope (optional) The scope of the function
9544          * @return {Roo.KeyMap} The KeyMap created
9545          */
9546         addKeyListener : function(key, fn, scope){
9547             var config;
9548             if(typeof key != "object" || key instanceof Array){
9549                 config = {
9550                     key: key,
9551                     fn: fn,
9552                     scope: scope
9553                 };
9554             }else{
9555                 config = {
9556                     key : key.key,
9557                     shift : key.shift,
9558                     ctrl : key.ctrl,
9559                     alt : key.alt,
9560                     fn: fn,
9561                     scope: scope
9562                 };
9563             }
9564             return new Roo.KeyMap(this, config);
9565         },
9566
9567         /**
9568          * Creates a KeyMap for this element
9569          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9570          * @return {Roo.KeyMap} The KeyMap created
9571          */
9572         addKeyMap : function(config){
9573             return new Roo.KeyMap(this, config);
9574         },
9575
9576         /**
9577          * Returns true if this element is scrollable.
9578          * @return {Boolean}
9579          */
9580          isScrollable : function(){
9581             var dom = this.dom;
9582             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9583         },
9584
9585         /**
9586          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9587          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9588          * @param {Number} value The new scroll value
9589          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9590          * @return {Element} this
9591          */
9592
9593         scrollTo : function(side, value, animate){
9594             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9595             if(!animate || !A){
9596                 this.dom[prop] = value;
9597             }else{
9598                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9599                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9600             }
9601             return this;
9602         },
9603
9604         /**
9605          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9606          * within this element's scrollable range.
9607          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9608          * @param {Number} distance How far to scroll the element in pixels
9609          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9610          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9611          * was scrolled as far as it could go.
9612          */
9613          scroll : function(direction, distance, animate){
9614              if(!this.isScrollable()){
9615                  return;
9616              }
9617              var el = this.dom;
9618              var l = el.scrollLeft, t = el.scrollTop;
9619              var w = el.scrollWidth, h = el.scrollHeight;
9620              var cw = el.clientWidth, ch = el.clientHeight;
9621              direction = direction.toLowerCase();
9622              var scrolled = false;
9623              var a = this.preanim(arguments, 2);
9624              switch(direction){
9625                  case "l":
9626                  case "left":
9627                      if(w - l > cw){
9628                          var v = Math.min(l + distance, w-cw);
9629                          this.scrollTo("left", v, a);
9630                          scrolled = true;
9631                      }
9632                      break;
9633                 case "r":
9634                 case "right":
9635                      if(l > 0){
9636                          var v = Math.max(l - distance, 0);
9637                          this.scrollTo("left", v, a);
9638                          scrolled = true;
9639                      }
9640                      break;
9641                 case "t":
9642                 case "top":
9643                 case "up":
9644                      if(t > 0){
9645                          var v = Math.max(t - distance, 0);
9646                          this.scrollTo("top", v, a);
9647                          scrolled = true;
9648                      }
9649                      break;
9650                 case "b":
9651                 case "bottom":
9652                 case "down":
9653                      if(h - t > ch){
9654                          var v = Math.min(t + distance, h-ch);
9655                          this.scrollTo("top", v, a);
9656                          scrolled = true;
9657                      }
9658                      break;
9659              }
9660              return scrolled;
9661         },
9662
9663         /**
9664          * Translates the passed page coordinates into left/top css values for this element
9665          * @param {Number/Array} x The page x or an array containing [x, y]
9666          * @param {Number} y The page y
9667          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9668          */
9669         translatePoints : function(x, y){
9670             if(typeof x == 'object' || x instanceof Array){
9671                 y = x[1]; x = x[0];
9672             }
9673             var p = this.getStyle('position');
9674             var o = this.getXY();
9675
9676             var l = parseInt(this.getStyle('left'), 10);
9677             var t = parseInt(this.getStyle('top'), 10);
9678
9679             if(isNaN(l)){
9680                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9681             }
9682             if(isNaN(t)){
9683                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9684             }
9685
9686             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9687         },
9688
9689         /**
9690          * Returns the current scroll position of the element.
9691          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9692          */
9693         getScroll : function(){
9694             var d = this.dom, doc = document;
9695             if(d == doc || d == doc.body){
9696                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9697                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9698                 return {left: l, top: t};
9699             }else{
9700                 return {left: d.scrollLeft, top: d.scrollTop};
9701             }
9702         },
9703
9704         /**
9705          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9706          * are convert to standard 6 digit hex color.
9707          * @param {String} attr The css attribute
9708          * @param {String} defaultValue The default value to use when a valid color isn't found
9709          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9710          * YUI color anims.
9711          */
9712         getColor : function(attr, defaultValue, prefix){
9713             var v = this.getStyle(attr);
9714             if(!v || v == "transparent" || v == "inherit") {
9715                 return defaultValue;
9716             }
9717             var color = typeof prefix == "undefined" ? "#" : prefix;
9718             if(v.substr(0, 4) == "rgb("){
9719                 var rvs = v.slice(4, v.length -1).split(",");
9720                 for(var i = 0; i < 3; i++){
9721                     var h = parseInt(rvs[i]).toString(16);
9722                     if(h < 16){
9723                         h = "0" + h;
9724                     }
9725                     color += h;
9726                 }
9727             } else {
9728                 if(v.substr(0, 1) == "#"){
9729                     if(v.length == 4) {
9730                         for(var i = 1; i < 4; i++){
9731                             var c = v.charAt(i);
9732                             color +=  c + c;
9733                         }
9734                     }else if(v.length == 7){
9735                         color += v.substr(1);
9736                     }
9737                 }
9738             }
9739             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9740         },
9741
9742         /**
9743          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9744          * gradient background, rounded corners and a 4-way shadow.
9745          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9746          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9747          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9748          * @return {Roo.Element} this
9749          */
9750         boxWrap : function(cls){
9751             cls = cls || 'x-box';
9752             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9753             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9754             return el;
9755         },
9756
9757         /**
9758          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9759          * @param {String} namespace The namespace in which to look for the attribute
9760          * @param {String} name The attribute name
9761          * @return {String} The attribute value
9762          */
9763         getAttributeNS : Roo.isIE ? function(ns, name){
9764             var d = this.dom;
9765             var type = typeof d[ns+":"+name];
9766             if(type != 'undefined' && type != 'unknown'){
9767                 return d[ns+":"+name];
9768             }
9769             return d[name];
9770         } : function(ns, name){
9771             var d = this.dom;
9772             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9773         },
9774         
9775         
9776         /**
9777          * Sets or Returns the value the dom attribute value
9778          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9779          * @param {String} value (optional) The value to set the attribute to
9780          * @return {String} The attribute value
9781          */
9782         attr : function(name){
9783             if (arguments.length > 1) {
9784                 this.dom.setAttribute(name, arguments[1]);
9785                 return arguments[1];
9786             }
9787             if (typeof(name) == 'object') {
9788                 for(var i in name) {
9789                     this.attr(i, name[i]);
9790                 }
9791                 return name;
9792             }
9793             
9794             
9795             if (!this.dom.hasAttribute(name)) {
9796                 return undefined;
9797             }
9798             return this.dom.getAttribute(name);
9799         }
9800         
9801         
9802         
9803     };
9804
9805     var ep = El.prototype;
9806
9807     /**
9808      * Appends an event handler (Shorthand for addListener)
9809      * @param {String}   eventName     The type of event to append
9810      * @param {Function} fn        The method the event invokes
9811      * @param {Object} scope       (optional) The scope (this object) of the fn
9812      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9813      * @method
9814      */
9815     ep.on = ep.addListener;
9816         // backwards compat
9817     ep.mon = ep.addListener;
9818
9819     /**
9820      * Removes an event handler from this element (shorthand for removeListener)
9821      * @param {String} eventName the type of event to remove
9822      * @param {Function} fn the method the event invokes
9823      * @return {Roo.Element} this
9824      * @method
9825      */
9826     ep.un = ep.removeListener;
9827
9828     /**
9829      * true to automatically adjust width and height settings for box-model issues (default to true)
9830      */
9831     ep.autoBoxAdjust = true;
9832
9833     // private
9834     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9835
9836     // private
9837     El.addUnits = function(v, defaultUnit){
9838         if(v === "" || v == "auto"){
9839             return v;
9840         }
9841         if(v === undefined){
9842             return '';
9843         }
9844         if(typeof v == "number" || !El.unitPattern.test(v)){
9845             return v + (defaultUnit || 'px');
9846         }
9847         return v;
9848     };
9849
9850     // special markup used throughout Roo when box wrapping elements
9851     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9852     /**
9853      * Visibility mode constant - Use visibility to hide element
9854      * @static
9855      * @type Number
9856      */
9857     El.VISIBILITY = 1;
9858     /**
9859      * Visibility mode constant - Use display to hide element
9860      * @static
9861      * @type Number
9862      */
9863     El.DISPLAY = 2;
9864
9865     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9866     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9867     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9868
9869
9870
9871     /**
9872      * @private
9873      */
9874     El.cache = {};
9875
9876     var docEl;
9877
9878     /**
9879      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9880      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9881      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9882      * @return {Element} The Element object
9883      * @static
9884      */
9885     El.get = function(el){
9886         var ex, elm, id;
9887         if(!el){ return null; }
9888         if(typeof el == "string"){ // element id
9889             if(!(elm = document.getElementById(el))){
9890                 return null;
9891             }
9892             if(ex = El.cache[el]){
9893                 ex.dom = elm;
9894             }else{
9895                 ex = El.cache[el] = new El(elm);
9896             }
9897             return ex;
9898         }else if(el.tagName){ // dom element
9899             if(!(id = el.id)){
9900                 id = Roo.id(el);
9901             }
9902             if(ex = El.cache[id]){
9903                 ex.dom = el;
9904             }else{
9905                 ex = El.cache[id] = new El(el);
9906             }
9907             return ex;
9908         }else if(el instanceof El){
9909             if(el != docEl){
9910                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9911                                                               // catch case where it hasn't been appended
9912                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9913             }
9914             return el;
9915         }else if(el.isComposite){
9916             return el;
9917         }else if(el instanceof Array){
9918             return El.select(el);
9919         }else if(el == document){
9920             // create a bogus element object representing the document object
9921             if(!docEl){
9922                 var f = function(){};
9923                 f.prototype = El.prototype;
9924                 docEl = new f();
9925                 docEl.dom = document;
9926             }
9927             return docEl;
9928         }
9929         return null;
9930     };
9931
9932     // private
9933     El.uncache = function(el){
9934         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9935             if(a[i]){
9936                 delete El.cache[a[i].id || a[i]];
9937             }
9938         }
9939     };
9940
9941     // private
9942     // Garbage collection - uncache elements/purge listeners on orphaned elements
9943     // so we don't hold a reference and cause the browser to retain them
9944     El.garbageCollect = function(){
9945         if(!Roo.enableGarbageCollector){
9946             clearInterval(El.collectorThread);
9947             return;
9948         }
9949         for(var eid in El.cache){
9950             var el = El.cache[eid], d = el.dom;
9951             // -------------------------------------------------------
9952             // Determining what is garbage:
9953             // -------------------------------------------------------
9954             // !d
9955             // dom node is null, definitely garbage
9956             // -------------------------------------------------------
9957             // !d.parentNode
9958             // no parentNode == direct orphan, definitely garbage
9959             // -------------------------------------------------------
9960             // !d.offsetParent && !document.getElementById(eid)
9961             // display none elements have no offsetParent so we will
9962             // also try to look it up by it's id. However, check
9963             // offsetParent first so we don't do unneeded lookups.
9964             // This enables collection of elements that are not orphans
9965             // directly, but somewhere up the line they have an orphan
9966             // parent.
9967             // -------------------------------------------------------
9968             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9969                 delete El.cache[eid];
9970                 if(d && Roo.enableListenerCollection){
9971                     E.purgeElement(d);
9972                 }
9973             }
9974         }
9975     }
9976     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9977
9978
9979     // dom is optional
9980     El.Flyweight = function(dom){
9981         this.dom = dom;
9982     };
9983     El.Flyweight.prototype = El.prototype;
9984
9985     El._flyweights = {};
9986     /**
9987      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9988      * the dom node can be overwritten by other code.
9989      * @param {String/HTMLElement} el The dom node or id
9990      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9991      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9992      * @static
9993      * @return {Element} The shared Element object
9994      */
9995     El.fly = function(el, named){
9996         named = named || '_global';
9997         el = Roo.getDom(el);
9998         if(!el){
9999             return null;
10000         }
10001         if(!El._flyweights[named]){
10002             El._flyweights[named] = new El.Flyweight();
10003         }
10004         El._flyweights[named].dom = el;
10005         return El._flyweights[named];
10006     };
10007
10008     /**
10009      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10010      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10011      * Shorthand of {@link Roo.Element#get}
10012      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10013      * @return {Element} The Element object
10014      * @member Roo
10015      * @method get
10016      */
10017     Roo.get = El.get;
10018     /**
10019      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10020      * the dom node can be overwritten by other code.
10021      * Shorthand of {@link Roo.Element#fly}
10022      * @param {String/HTMLElement} el The dom node or id
10023      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10024      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10025      * @static
10026      * @return {Element} The shared Element object
10027      * @member Roo
10028      * @method fly
10029      */
10030     Roo.fly = El.fly;
10031
10032     // speedy lookup for elements never to box adjust
10033     var noBoxAdjust = Roo.isStrict ? {
10034         select:1
10035     } : {
10036         input:1, select:1, textarea:1
10037     };
10038     if(Roo.isIE || Roo.isGecko){
10039         noBoxAdjust['button'] = 1;
10040     }
10041
10042
10043     Roo.EventManager.on(window, 'unload', function(){
10044         delete El.cache;
10045         delete El._flyweights;
10046     });
10047 })();
10048
10049
10050
10051
10052 if(Roo.DomQuery){
10053     Roo.Element.selectorFunction = Roo.DomQuery.select;
10054 }
10055
10056 Roo.Element.select = function(selector, unique, root){
10057     var els;
10058     if(typeof selector == "string"){
10059         els = Roo.Element.selectorFunction(selector, root);
10060     }else if(selector.length !== undefined){
10061         els = selector;
10062     }else{
10063         throw "Invalid selector";
10064     }
10065     if(unique === true){
10066         return new Roo.CompositeElement(els);
10067     }else{
10068         return new Roo.CompositeElementLite(els);
10069     }
10070 };
10071 /**
10072  * Selects elements based on the passed CSS selector to enable working on them as 1.
10073  * @param {String/Array} selector The CSS selector or an array of elements
10074  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10075  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10076  * @return {CompositeElementLite/CompositeElement}
10077  * @member Roo
10078  * @method select
10079  */
10080 Roo.select = Roo.Element.select;
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095 /*
10096  * Based on:
10097  * Ext JS Library 1.1.1
10098  * Copyright(c) 2006-2007, Ext JS, LLC.
10099  *
10100  * Originally Released Under LGPL - original licence link has changed is not relivant.
10101  *
10102  * Fork - LGPL
10103  * <script type="text/javascript">
10104  */
10105
10106
10107
10108 //Notifies Element that fx methods are available
10109 Roo.enableFx = true;
10110
10111 /**
10112  * @class Roo.Fx
10113  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10114  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10115  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10116  * Element effects to work.</p><br/>
10117  *
10118  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10119  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10120  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10121  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10122  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10123  * expected results and should be done with care.</p><br/>
10124  *
10125  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10126  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10127 <pre>
10128 Value  Description
10129 -----  -----------------------------
10130 tl     The top left corner
10131 t      The center of the top edge
10132 tr     The top right corner
10133 l      The center of the left edge
10134 r      The center of the right edge
10135 bl     The bottom left corner
10136 b      The center of the bottom edge
10137 br     The bottom right corner
10138 </pre>
10139  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10140  * below are common options that can be passed to any Fx method.</b>
10141  * @cfg {Function} callback A function called when the effect is finished
10142  * @cfg {Object} scope The scope of the effect function
10143  * @cfg {String} easing A valid Easing value for the effect
10144  * @cfg {String} afterCls A css class to apply after the effect
10145  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10146  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10147  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10148  * effects that end with the element being visually hidden, ignored otherwise)
10149  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10150  * a function which returns such a specification that will be applied to the Element after the effect finishes
10151  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10152  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
10153  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10154  */
10155 Roo.Fx = {
10156         /**
10157          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10158          * origin for the slide effect.  This function automatically handles wrapping the element with
10159          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10160          * Usage:
10161          *<pre><code>
10162 // default: slide the element in from the top
10163 el.slideIn();
10164
10165 // custom: slide the element in from the right with a 2-second duration
10166 el.slideIn('r', { duration: 2 });
10167
10168 // common config options shown with default values
10169 el.slideIn('t', {
10170     easing: 'easeOut',
10171     duration: .5
10172 });
10173 </code></pre>
10174          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10175          * @param {Object} options (optional) Object literal with any of the Fx config options
10176          * @return {Roo.Element} The Element
10177          */
10178     slideIn : function(anchor, o){
10179         var el = this.getFxEl();
10180         o = o || {};
10181
10182         el.queueFx(o, function(){
10183
10184             anchor = anchor || "t";
10185
10186             // fix display to visibility
10187             this.fixDisplay();
10188
10189             // restore values after effect
10190             var r = this.getFxRestore();
10191             var b = this.getBox();
10192             // fixed size for slide
10193             this.setSize(b);
10194
10195             // wrap if needed
10196             var wrap = this.fxWrap(r.pos, o, "hidden");
10197
10198             var st = this.dom.style;
10199             st.visibility = "visible";
10200             st.position = "absolute";
10201
10202             // clear out temp styles after slide and unwrap
10203             var after = function(){
10204                 el.fxUnwrap(wrap, r.pos, o);
10205                 st.width = r.width;
10206                 st.height = r.height;
10207                 el.afterFx(o);
10208             };
10209             // time to calc the positions
10210             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10211
10212             switch(anchor.toLowerCase()){
10213                 case "t":
10214                     wrap.setSize(b.width, 0);
10215                     st.left = st.bottom = "0";
10216                     a = {height: bh};
10217                 break;
10218                 case "l":
10219                     wrap.setSize(0, b.height);
10220                     st.right = st.top = "0";
10221                     a = {width: bw};
10222                 break;
10223                 case "r":
10224                     wrap.setSize(0, b.height);
10225                     wrap.setX(b.right);
10226                     st.left = st.top = "0";
10227                     a = {width: bw, points: pt};
10228                 break;
10229                 case "b":
10230                     wrap.setSize(b.width, 0);
10231                     wrap.setY(b.bottom);
10232                     st.left = st.top = "0";
10233                     a = {height: bh, points: pt};
10234                 break;
10235                 case "tl":
10236                     wrap.setSize(0, 0);
10237                     st.right = st.bottom = "0";
10238                     a = {width: bw, height: bh};
10239                 break;
10240                 case "bl":
10241                     wrap.setSize(0, 0);
10242                     wrap.setY(b.y+b.height);
10243                     st.right = st.top = "0";
10244                     a = {width: bw, height: bh, points: pt};
10245                 break;
10246                 case "br":
10247                     wrap.setSize(0, 0);
10248                     wrap.setXY([b.right, b.bottom]);
10249                     st.left = st.top = "0";
10250                     a = {width: bw, height: bh, points: pt};
10251                 break;
10252                 case "tr":
10253                     wrap.setSize(0, 0);
10254                     wrap.setX(b.x+b.width);
10255                     st.left = st.bottom = "0";
10256                     a = {width: bw, height: bh, points: pt};
10257                 break;
10258             }
10259             this.dom.style.visibility = "visible";
10260             wrap.show();
10261
10262             arguments.callee.anim = wrap.fxanim(a,
10263                 o,
10264                 'motion',
10265                 .5,
10266                 'easeOut', after);
10267         });
10268         return this;
10269     },
10270     
10271         /**
10272          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10273          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10274          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10275          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10276          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10277          * Usage:
10278          *<pre><code>
10279 // default: slide the element out to the top
10280 el.slideOut();
10281
10282 // custom: slide the element out to the right with a 2-second duration
10283 el.slideOut('r', { duration: 2 });
10284
10285 // common config options shown with default values
10286 el.slideOut('t', {
10287     easing: 'easeOut',
10288     duration: .5,
10289     remove: false,
10290     useDisplay: false
10291 });
10292 </code></pre>
10293          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10294          * @param {Object} options (optional) Object literal with any of the Fx config options
10295          * @return {Roo.Element} The Element
10296          */
10297     slideOut : function(anchor, o){
10298         var el = this.getFxEl();
10299         o = o || {};
10300
10301         el.queueFx(o, function(){
10302
10303             anchor = anchor || "t";
10304
10305             // restore values after effect
10306             var r = this.getFxRestore();
10307             
10308             var b = this.getBox();
10309             // fixed size for slide
10310             this.setSize(b);
10311
10312             // wrap if needed
10313             var wrap = this.fxWrap(r.pos, o, "visible");
10314
10315             var st = this.dom.style;
10316             st.visibility = "visible";
10317             st.position = "absolute";
10318
10319             wrap.setSize(b);
10320
10321             var after = function(){
10322                 if(o.useDisplay){
10323                     el.setDisplayed(false);
10324                 }else{
10325                     el.hide();
10326                 }
10327
10328                 el.fxUnwrap(wrap, r.pos, o);
10329
10330                 st.width = r.width;
10331                 st.height = r.height;
10332
10333                 el.afterFx(o);
10334             };
10335
10336             var a, zero = {to: 0};
10337             switch(anchor.toLowerCase()){
10338                 case "t":
10339                     st.left = st.bottom = "0";
10340                     a = {height: zero};
10341                 break;
10342                 case "l":
10343                     st.right = st.top = "0";
10344                     a = {width: zero};
10345                 break;
10346                 case "r":
10347                     st.left = st.top = "0";
10348                     a = {width: zero, points: {to:[b.right, b.y]}};
10349                 break;
10350                 case "b":
10351                     st.left = st.top = "0";
10352                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10353                 break;
10354                 case "tl":
10355                     st.right = st.bottom = "0";
10356                     a = {width: zero, height: zero};
10357                 break;
10358                 case "bl":
10359                     st.right = st.top = "0";
10360                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10361                 break;
10362                 case "br":
10363                     st.left = st.top = "0";
10364                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10365                 break;
10366                 case "tr":
10367                     st.left = st.bottom = "0";
10368                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10369                 break;
10370             }
10371
10372             arguments.callee.anim = wrap.fxanim(a,
10373                 o,
10374                 'motion',
10375                 .5,
10376                 "easeOut", after);
10377         });
10378         return this;
10379     },
10380
10381         /**
10382          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10383          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10384          * The element must be removed from the DOM using the 'remove' config option if desired.
10385          * Usage:
10386          *<pre><code>
10387 // default
10388 el.puff();
10389
10390 // common config options shown with default values
10391 el.puff({
10392     easing: 'easeOut',
10393     duration: .5,
10394     remove: false,
10395     useDisplay: false
10396 });
10397 </code></pre>
10398          * @param {Object} options (optional) Object literal with any of the Fx config options
10399          * @return {Roo.Element} The Element
10400          */
10401     puff : function(o){
10402         var el = this.getFxEl();
10403         o = o || {};
10404
10405         el.queueFx(o, function(){
10406             this.clearOpacity();
10407             this.show();
10408
10409             // restore values after effect
10410             var r = this.getFxRestore();
10411             var st = this.dom.style;
10412
10413             var after = function(){
10414                 if(o.useDisplay){
10415                     el.setDisplayed(false);
10416                 }else{
10417                     el.hide();
10418                 }
10419
10420                 el.clearOpacity();
10421
10422                 el.setPositioning(r.pos);
10423                 st.width = r.width;
10424                 st.height = r.height;
10425                 st.fontSize = '';
10426                 el.afterFx(o);
10427             };
10428
10429             var width = this.getWidth();
10430             var height = this.getHeight();
10431
10432             arguments.callee.anim = this.fxanim({
10433                     width : {to: this.adjustWidth(width * 2)},
10434                     height : {to: this.adjustHeight(height * 2)},
10435                     points : {by: [-(width * .5), -(height * .5)]},
10436                     opacity : {to: 0},
10437                     fontSize: {to:200, unit: "%"}
10438                 },
10439                 o,
10440                 'motion',
10441                 .5,
10442                 "easeOut", after);
10443         });
10444         return this;
10445     },
10446
10447         /**
10448          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10449          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10450          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10451          * Usage:
10452          *<pre><code>
10453 // default
10454 el.switchOff();
10455
10456 // all config options shown with default values
10457 el.switchOff({
10458     easing: 'easeIn',
10459     duration: .3,
10460     remove: false,
10461     useDisplay: false
10462 });
10463 </code></pre>
10464          * @param {Object} options (optional) Object literal with any of the Fx config options
10465          * @return {Roo.Element} The Element
10466          */
10467     switchOff : function(o){
10468         var el = this.getFxEl();
10469         o = o || {};
10470
10471         el.queueFx(o, function(){
10472             this.clearOpacity();
10473             this.clip();
10474
10475             // restore values after effect
10476             var r = this.getFxRestore();
10477             var st = this.dom.style;
10478
10479             var after = function(){
10480                 if(o.useDisplay){
10481                     el.setDisplayed(false);
10482                 }else{
10483                     el.hide();
10484                 }
10485
10486                 el.clearOpacity();
10487                 el.setPositioning(r.pos);
10488                 st.width = r.width;
10489                 st.height = r.height;
10490
10491                 el.afterFx(o);
10492             };
10493
10494             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10495                 this.clearOpacity();
10496                 (function(){
10497                     this.fxanim({
10498                         height:{to:1},
10499                         points:{by:[0, this.getHeight() * .5]}
10500                     }, o, 'motion', 0.3, 'easeIn', after);
10501                 }).defer(100, this);
10502             });
10503         });
10504         return this;
10505     },
10506
10507     /**
10508      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10509      * changed using the "attr" config option) and then fading back to the original color. If no original
10510      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10511      * Usage:
10512 <pre><code>
10513 // default: highlight background to yellow
10514 el.highlight();
10515
10516 // custom: highlight foreground text to blue for 2 seconds
10517 el.highlight("0000ff", { attr: 'color', duration: 2 });
10518
10519 // common config options shown with default values
10520 el.highlight("ffff9c", {
10521     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10522     endColor: (current color) or "ffffff",
10523     easing: 'easeIn',
10524     duration: 1
10525 });
10526 </code></pre>
10527      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10528      * @param {Object} options (optional) Object literal with any of the Fx config options
10529      * @return {Roo.Element} The Element
10530      */ 
10531     highlight : function(color, o){
10532         var el = this.getFxEl();
10533         o = o || {};
10534
10535         el.queueFx(o, function(){
10536             color = color || "ffff9c";
10537             attr = o.attr || "backgroundColor";
10538
10539             this.clearOpacity();
10540             this.show();
10541
10542             var origColor = this.getColor(attr);
10543             var restoreColor = this.dom.style[attr];
10544             endColor = (o.endColor || origColor) || "ffffff";
10545
10546             var after = function(){
10547                 el.dom.style[attr] = restoreColor;
10548                 el.afterFx(o);
10549             };
10550
10551             var a = {};
10552             a[attr] = {from: color, to: endColor};
10553             arguments.callee.anim = this.fxanim(a,
10554                 o,
10555                 'color',
10556                 1,
10557                 'easeIn', after);
10558         });
10559         return this;
10560     },
10561
10562    /**
10563     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10564     * Usage:
10565 <pre><code>
10566 // default: a single light blue ripple
10567 el.frame();
10568
10569 // custom: 3 red ripples lasting 3 seconds total
10570 el.frame("ff0000", 3, { duration: 3 });
10571
10572 // common config options shown with default values
10573 el.frame("C3DAF9", 1, {
10574     duration: 1 //duration of entire animation (not each individual ripple)
10575     // Note: Easing is not configurable and will be ignored if included
10576 });
10577 </code></pre>
10578     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10579     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10580     * @param {Object} options (optional) Object literal with any of the Fx config options
10581     * @return {Roo.Element} The Element
10582     */
10583     frame : function(color, count, o){
10584         var el = this.getFxEl();
10585         o = o || {};
10586
10587         el.queueFx(o, function(){
10588             color = color || "#C3DAF9";
10589             if(color.length == 6){
10590                 color = "#" + color;
10591             }
10592             count = count || 1;
10593             duration = o.duration || 1;
10594             this.show();
10595
10596             var b = this.getBox();
10597             var animFn = function(){
10598                 var proxy = this.createProxy({
10599
10600                      style:{
10601                         visbility:"hidden",
10602                         position:"absolute",
10603                         "z-index":"35000", // yee haw
10604                         border:"0px solid " + color
10605                      }
10606                   });
10607                 var scale = Roo.isBorderBox ? 2 : 1;
10608                 proxy.animate({
10609                     top:{from:b.y, to:b.y - 20},
10610                     left:{from:b.x, to:b.x - 20},
10611                     borderWidth:{from:0, to:10},
10612                     opacity:{from:1, to:0},
10613                     height:{from:b.height, to:(b.height + (20*scale))},
10614                     width:{from:b.width, to:(b.width + (20*scale))}
10615                 }, duration, function(){
10616                     proxy.remove();
10617                 });
10618                 if(--count > 0){
10619                      animFn.defer((duration/2)*1000, this);
10620                 }else{
10621                     el.afterFx(o);
10622                 }
10623             };
10624             animFn.call(this);
10625         });
10626         return this;
10627     },
10628
10629    /**
10630     * Creates a pause before any subsequent queued effects begin.  If there are
10631     * no effects queued after the pause it will have no effect.
10632     * Usage:
10633 <pre><code>
10634 el.pause(1);
10635 </code></pre>
10636     * @param {Number} seconds The length of time to pause (in seconds)
10637     * @return {Roo.Element} The Element
10638     */
10639     pause : function(seconds){
10640         var el = this.getFxEl();
10641         var o = {};
10642
10643         el.queueFx(o, function(){
10644             setTimeout(function(){
10645                 el.afterFx(o);
10646             }, seconds * 1000);
10647         });
10648         return this;
10649     },
10650
10651    /**
10652     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10653     * using the "endOpacity" config option.
10654     * Usage:
10655 <pre><code>
10656 // default: fade in from opacity 0 to 100%
10657 el.fadeIn();
10658
10659 // custom: fade in from opacity 0 to 75% over 2 seconds
10660 el.fadeIn({ endOpacity: .75, duration: 2});
10661
10662 // common config options shown with default values
10663 el.fadeIn({
10664     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10665     easing: 'easeOut',
10666     duration: .5
10667 });
10668 </code></pre>
10669     * @param {Object} options (optional) Object literal with any of the Fx config options
10670     * @return {Roo.Element} The Element
10671     */
10672     fadeIn : function(o){
10673         var el = this.getFxEl();
10674         o = o || {};
10675         el.queueFx(o, function(){
10676             this.setOpacity(0);
10677             this.fixDisplay();
10678             this.dom.style.visibility = 'visible';
10679             var to = o.endOpacity || 1;
10680             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10681                 o, null, .5, "easeOut", function(){
10682                 if(to == 1){
10683                     this.clearOpacity();
10684                 }
10685                 el.afterFx(o);
10686             });
10687         });
10688         return this;
10689     },
10690
10691    /**
10692     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10693     * using the "endOpacity" config option.
10694     * Usage:
10695 <pre><code>
10696 // default: fade out from the element's current opacity to 0
10697 el.fadeOut();
10698
10699 // custom: fade out from the element's current opacity to 25% over 2 seconds
10700 el.fadeOut({ endOpacity: .25, duration: 2});
10701
10702 // common config options shown with default values
10703 el.fadeOut({
10704     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10705     easing: 'easeOut',
10706     duration: .5
10707     remove: false,
10708     useDisplay: false
10709 });
10710 </code></pre>
10711     * @param {Object} options (optional) Object literal with any of the Fx config options
10712     * @return {Roo.Element} The Element
10713     */
10714     fadeOut : function(o){
10715         var el = this.getFxEl();
10716         o = o || {};
10717         el.queueFx(o, function(){
10718             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10719                 o, null, .5, "easeOut", function(){
10720                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10721                      this.dom.style.display = "none";
10722                 }else{
10723                      this.dom.style.visibility = "hidden";
10724                 }
10725                 this.clearOpacity();
10726                 el.afterFx(o);
10727             });
10728         });
10729         return this;
10730     },
10731
10732    /**
10733     * Animates the transition of an element's dimensions from a starting height/width
10734     * to an ending height/width.
10735     * Usage:
10736 <pre><code>
10737 // change height and width to 100x100 pixels
10738 el.scale(100, 100);
10739
10740 // common config options shown with default values.  The height and width will default to
10741 // the element's existing values if passed as null.
10742 el.scale(
10743     [element's width],
10744     [element's height], {
10745     easing: 'easeOut',
10746     duration: .35
10747 });
10748 </code></pre>
10749     * @param {Number} width  The new width (pass undefined to keep the original width)
10750     * @param {Number} height  The new height (pass undefined to keep the original height)
10751     * @param {Object} options (optional) Object literal with any of the Fx config options
10752     * @return {Roo.Element} The Element
10753     */
10754     scale : function(w, h, o){
10755         this.shift(Roo.apply({}, o, {
10756             width: w,
10757             height: h
10758         }));
10759         return this;
10760     },
10761
10762    /**
10763     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10764     * Any of these properties not specified in the config object will not be changed.  This effect 
10765     * requires that at least one new dimension, position or opacity setting must be passed in on
10766     * the config object in order for the function to have any effect.
10767     * Usage:
10768 <pre><code>
10769 // slide the element horizontally to x position 200 while changing the height and opacity
10770 el.shift({ x: 200, height: 50, opacity: .8 });
10771
10772 // common config options shown with default values.
10773 el.shift({
10774     width: [element's width],
10775     height: [element's height],
10776     x: [element's x position],
10777     y: [element's y position],
10778     opacity: [element's opacity],
10779     easing: 'easeOut',
10780     duration: .35
10781 });
10782 </code></pre>
10783     * @param {Object} options  Object literal with any of the Fx config options
10784     * @return {Roo.Element} The Element
10785     */
10786     shift : function(o){
10787         var el = this.getFxEl();
10788         o = o || {};
10789         el.queueFx(o, function(){
10790             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10791             if(w !== undefined){
10792                 a.width = {to: this.adjustWidth(w)};
10793             }
10794             if(h !== undefined){
10795                 a.height = {to: this.adjustHeight(h)};
10796             }
10797             if(x !== undefined || y !== undefined){
10798                 a.points = {to: [
10799                     x !== undefined ? x : this.getX(),
10800                     y !== undefined ? y : this.getY()
10801                 ]};
10802             }
10803             if(op !== undefined){
10804                 a.opacity = {to: op};
10805             }
10806             if(o.xy !== undefined){
10807                 a.points = {to: o.xy};
10808             }
10809             arguments.callee.anim = this.fxanim(a,
10810                 o, 'motion', .35, "easeOut", function(){
10811                 el.afterFx(o);
10812             });
10813         });
10814         return this;
10815     },
10816
10817         /**
10818          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10819          * ending point of the effect.
10820          * Usage:
10821          *<pre><code>
10822 // default: slide the element downward while fading out
10823 el.ghost();
10824
10825 // custom: slide the element out to the right with a 2-second duration
10826 el.ghost('r', { duration: 2 });
10827
10828 // common config options shown with default values
10829 el.ghost('b', {
10830     easing: 'easeOut',
10831     duration: .5
10832     remove: false,
10833     useDisplay: false
10834 });
10835 </code></pre>
10836          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10837          * @param {Object} options (optional) Object literal with any of the Fx config options
10838          * @return {Roo.Element} The Element
10839          */
10840     ghost : function(anchor, o){
10841         var el = this.getFxEl();
10842         o = o || {};
10843
10844         el.queueFx(o, function(){
10845             anchor = anchor || "b";
10846
10847             // restore values after effect
10848             var r = this.getFxRestore();
10849             var w = this.getWidth(),
10850                 h = this.getHeight();
10851
10852             var st = this.dom.style;
10853
10854             var after = function(){
10855                 if(o.useDisplay){
10856                     el.setDisplayed(false);
10857                 }else{
10858                     el.hide();
10859                 }
10860
10861                 el.clearOpacity();
10862                 el.setPositioning(r.pos);
10863                 st.width = r.width;
10864                 st.height = r.height;
10865
10866                 el.afterFx(o);
10867             };
10868
10869             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10870             switch(anchor.toLowerCase()){
10871                 case "t":
10872                     pt.by = [0, -h];
10873                 break;
10874                 case "l":
10875                     pt.by = [-w, 0];
10876                 break;
10877                 case "r":
10878                     pt.by = [w, 0];
10879                 break;
10880                 case "b":
10881                     pt.by = [0, h];
10882                 break;
10883                 case "tl":
10884                     pt.by = [-w, -h];
10885                 break;
10886                 case "bl":
10887                     pt.by = [-w, h];
10888                 break;
10889                 case "br":
10890                     pt.by = [w, h];
10891                 break;
10892                 case "tr":
10893                     pt.by = [w, -h];
10894                 break;
10895             }
10896
10897             arguments.callee.anim = this.fxanim(a,
10898                 o,
10899                 'motion',
10900                 .5,
10901                 "easeOut", after);
10902         });
10903         return this;
10904     },
10905
10906         /**
10907          * Ensures that all effects queued after syncFx is called on the element are
10908          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10909          * @return {Roo.Element} The Element
10910          */
10911     syncFx : function(){
10912         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10913             block : false,
10914             concurrent : true,
10915             stopFx : false
10916         });
10917         return this;
10918     },
10919
10920         /**
10921          * Ensures that all effects queued after sequenceFx is called on the element are
10922          * run in sequence.  This is the opposite of {@link #syncFx}.
10923          * @return {Roo.Element} The Element
10924          */
10925     sequenceFx : function(){
10926         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10927             block : false,
10928             concurrent : false,
10929             stopFx : false
10930         });
10931         return this;
10932     },
10933
10934         /* @private */
10935     nextFx : function(){
10936         var ef = this.fxQueue[0];
10937         if(ef){
10938             ef.call(this);
10939         }
10940     },
10941
10942         /**
10943          * Returns true if the element has any effects actively running or queued, else returns false.
10944          * @return {Boolean} True if element has active effects, else false
10945          */
10946     hasActiveFx : function(){
10947         return this.fxQueue && this.fxQueue[0];
10948     },
10949
10950         /**
10951          * Stops any running effects and clears the element's internal effects queue if it contains
10952          * any additional effects that haven't started yet.
10953          * @return {Roo.Element} The Element
10954          */
10955     stopFx : function(){
10956         if(this.hasActiveFx()){
10957             var cur = this.fxQueue[0];
10958             if(cur && cur.anim && cur.anim.isAnimated()){
10959                 this.fxQueue = [cur]; // clear out others
10960                 cur.anim.stop(true);
10961             }
10962         }
10963         return this;
10964     },
10965
10966         /* @private */
10967     beforeFx : function(o){
10968         if(this.hasActiveFx() && !o.concurrent){
10969            if(o.stopFx){
10970                this.stopFx();
10971                return true;
10972            }
10973            return false;
10974         }
10975         return true;
10976     },
10977
10978         /**
10979          * Returns true if the element is currently blocking so that no other effect can be queued
10980          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10981          * used to ensure that an effect initiated by a user action runs to completion prior to the
10982          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10983          * @return {Boolean} True if blocking, else false
10984          */
10985     hasFxBlock : function(){
10986         var q = this.fxQueue;
10987         return q && q[0] && q[0].block;
10988     },
10989
10990         /* @private */
10991     queueFx : function(o, fn){
10992         if(!this.fxQueue){
10993             this.fxQueue = [];
10994         }
10995         if(!this.hasFxBlock()){
10996             Roo.applyIf(o, this.fxDefaults);
10997             if(!o.concurrent){
10998                 var run = this.beforeFx(o);
10999                 fn.block = o.block;
11000                 this.fxQueue.push(fn);
11001                 if(run){
11002                     this.nextFx();
11003                 }
11004             }else{
11005                 fn.call(this);
11006             }
11007         }
11008         return this;
11009     },
11010
11011         /* @private */
11012     fxWrap : function(pos, o, vis){
11013         var wrap;
11014         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11015             var wrapXY;
11016             if(o.fixPosition){
11017                 wrapXY = this.getXY();
11018             }
11019             var div = document.createElement("div");
11020             div.style.visibility = vis;
11021             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11022             wrap.setPositioning(pos);
11023             if(wrap.getStyle("position") == "static"){
11024                 wrap.position("relative");
11025             }
11026             this.clearPositioning('auto');
11027             wrap.clip();
11028             wrap.dom.appendChild(this.dom);
11029             if(wrapXY){
11030                 wrap.setXY(wrapXY);
11031             }
11032         }
11033         return wrap;
11034     },
11035
11036         /* @private */
11037     fxUnwrap : function(wrap, pos, o){
11038         this.clearPositioning();
11039         this.setPositioning(pos);
11040         if(!o.wrap){
11041             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11042             wrap.remove();
11043         }
11044     },
11045
11046         /* @private */
11047     getFxRestore : function(){
11048         var st = this.dom.style;
11049         return {pos: this.getPositioning(), width: st.width, height : st.height};
11050     },
11051
11052         /* @private */
11053     afterFx : function(o){
11054         if(o.afterStyle){
11055             this.applyStyles(o.afterStyle);
11056         }
11057         if(o.afterCls){
11058             this.addClass(o.afterCls);
11059         }
11060         if(o.remove === true){
11061             this.remove();
11062         }
11063         Roo.callback(o.callback, o.scope, [this]);
11064         if(!o.concurrent){
11065             this.fxQueue.shift();
11066             this.nextFx();
11067         }
11068     },
11069
11070         /* @private */
11071     getFxEl : function(){ // support for composite element fx
11072         return Roo.get(this.dom);
11073     },
11074
11075         /* @private */
11076     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11077         animType = animType || 'run';
11078         opt = opt || {};
11079         var anim = Roo.lib.Anim[animType](
11080             this.dom, args,
11081             (opt.duration || defaultDur) || .35,
11082             (opt.easing || defaultEase) || 'easeOut',
11083             function(){
11084                 Roo.callback(cb, this);
11085             },
11086             this
11087         );
11088         opt.anim = anim;
11089         return anim;
11090     }
11091 };
11092
11093 // backwords compat
11094 Roo.Fx.resize = Roo.Fx.scale;
11095
11096 //When included, Roo.Fx is automatically applied to Element so that all basic
11097 //effects are available directly via the Element API
11098 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11099  * Based on:
11100  * Ext JS Library 1.1.1
11101  * Copyright(c) 2006-2007, Ext JS, LLC.
11102  *
11103  * Originally Released Under LGPL - original licence link has changed is not relivant.
11104  *
11105  * Fork - LGPL
11106  * <script type="text/javascript">
11107  */
11108
11109
11110 /**
11111  * @class Roo.CompositeElement
11112  * Standard composite class. Creates a Roo.Element for every element in the collection.
11113  * <br><br>
11114  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11115  * actions will be performed on all the elements in this collection.</b>
11116  * <br><br>
11117  * All methods return <i>this</i> and can be chained.
11118  <pre><code>
11119  var els = Roo.select("#some-el div.some-class", true);
11120  // or select directly from an existing element
11121  var el = Roo.get('some-el');
11122  el.select('div.some-class', true);
11123
11124  els.setWidth(100); // all elements become 100 width
11125  els.hide(true); // all elements fade out and hide
11126  // or
11127  els.setWidth(100).hide(true);
11128  </code></pre>
11129  */
11130 Roo.CompositeElement = function(els){
11131     this.elements = [];
11132     this.addElements(els);
11133 };
11134 Roo.CompositeElement.prototype = {
11135     isComposite: true,
11136     addElements : function(els){
11137         if(!els) {
11138             return this;
11139         }
11140         if(typeof els == "string"){
11141             els = Roo.Element.selectorFunction(els);
11142         }
11143         var yels = this.elements;
11144         var index = yels.length-1;
11145         for(var i = 0, len = els.length; i < len; i++) {
11146                 yels[++index] = Roo.get(els[i]);
11147         }
11148         return this;
11149     },
11150
11151     /**
11152     * Clears this composite and adds the elements returned by the passed selector.
11153     * @param {String/Array} els A string CSS selector, an array of elements or an element
11154     * @return {CompositeElement} this
11155     */
11156     fill : function(els){
11157         this.elements = [];
11158         this.add(els);
11159         return this;
11160     },
11161
11162     /**
11163     * Filters this composite to only elements that match the passed selector.
11164     * @param {String} selector A string CSS selector
11165     * @param {Boolean} inverse return inverse filter (not matches)
11166     * @return {CompositeElement} this
11167     */
11168     filter : function(selector, inverse){
11169         var els = [];
11170         inverse = inverse || false;
11171         this.each(function(el){
11172             var match = inverse ? !el.is(selector) : el.is(selector);
11173             if(match){
11174                 els[els.length] = el.dom;
11175             }
11176         });
11177         this.fill(els);
11178         return this;
11179     },
11180
11181     invoke : function(fn, args){
11182         var els = this.elements;
11183         for(var i = 0, len = els.length; i < len; i++) {
11184                 Roo.Element.prototype[fn].apply(els[i], args);
11185         }
11186         return this;
11187     },
11188     /**
11189     * Adds elements to this composite.
11190     * @param {String/Array} els A string CSS selector, an array of elements or an element
11191     * @return {CompositeElement} this
11192     */
11193     add : function(els){
11194         if(typeof els == "string"){
11195             this.addElements(Roo.Element.selectorFunction(els));
11196         }else if(els.length !== undefined){
11197             this.addElements(els);
11198         }else{
11199             this.addElements([els]);
11200         }
11201         return this;
11202     },
11203     /**
11204     * Calls the passed function passing (el, this, index) for each element in this composite.
11205     * @param {Function} fn The function to call
11206     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11207     * @return {CompositeElement} this
11208     */
11209     each : function(fn, scope){
11210         var els = this.elements;
11211         for(var i = 0, len = els.length; i < len; i++){
11212             if(fn.call(scope || els[i], els[i], this, i) === false) {
11213                 break;
11214             }
11215         }
11216         return this;
11217     },
11218
11219     /**
11220      * Returns the Element object at the specified index
11221      * @param {Number} index
11222      * @return {Roo.Element}
11223      */
11224     item : function(index){
11225         return this.elements[index] || null;
11226     },
11227
11228     /**
11229      * Returns the first Element
11230      * @return {Roo.Element}
11231      */
11232     first : function(){
11233         return this.item(0);
11234     },
11235
11236     /**
11237      * Returns the last Element
11238      * @return {Roo.Element}
11239      */
11240     last : function(){
11241         return this.item(this.elements.length-1);
11242     },
11243
11244     /**
11245      * Returns the number of elements in this composite
11246      * @return Number
11247      */
11248     getCount : function(){
11249         return this.elements.length;
11250     },
11251
11252     /**
11253      * Returns true if this composite contains the passed element
11254      * @return Boolean
11255      */
11256     contains : function(el){
11257         return this.indexOf(el) !== -1;
11258     },
11259
11260     /**
11261      * Returns true if this composite contains the passed element
11262      * @return Boolean
11263      */
11264     indexOf : function(el){
11265         return this.elements.indexOf(Roo.get(el));
11266     },
11267
11268
11269     /**
11270     * Removes the specified element(s).
11271     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11272     * or an array of any of those.
11273     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11274     * @return {CompositeElement} this
11275     */
11276     removeElement : function(el, removeDom){
11277         if(el instanceof Array){
11278             for(var i = 0, len = el.length; i < len; i++){
11279                 this.removeElement(el[i]);
11280             }
11281             return this;
11282         }
11283         var index = typeof el == 'number' ? el : this.indexOf(el);
11284         if(index !== -1){
11285             if(removeDom){
11286                 var d = this.elements[index];
11287                 if(d.dom){
11288                     d.remove();
11289                 }else{
11290                     d.parentNode.removeChild(d);
11291                 }
11292             }
11293             this.elements.splice(index, 1);
11294         }
11295         return this;
11296     },
11297
11298     /**
11299     * Replaces the specified element with the passed element.
11300     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11301     * to replace.
11302     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11303     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11304     * @return {CompositeElement} this
11305     */
11306     replaceElement : function(el, replacement, domReplace){
11307         var index = typeof el == 'number' ? el : this.indexOf(el);
11308         if(index !== -1){
11309             if(domReplace){
11310                 this.elements[index].replaceWith(replacement);
11311             }else{
11312                 this.elements.splice(index, 1, Roo.get(replacement))
11313             }
11314         }
11315         return this;
11316     },
11317
11318     /**
11319      * Removes all elements.
11320      */
11321     clear : function(){
11322         this.elements = [];
11323     }
11324 };
11325 (function(){
11326     Roo.CompositeElement.createCall = function(proto, fnName){
11327         if(!proto[fnName]){
11328             proto[fnName] = function(){
11329                 return this.invoke(fnName, arguments);
11330             };
11331         }
11332     };
11333     for(var fnName in Roo.Element.prototype){
11334         if(typeof Roo.Element.prototype[fnName] == "function"){
11335             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11336         }
11337     };
11338 })();
11339 /*
11340  * Based on:
11341  * Ext JS Library 1.1.1
11342  * Copyright(c) 2006-2007, Ext JS, LLC.
11343  *
11344  * Originally Released Under LGPL - original licence link has changed is not relivant.
11345  *
11346  * Fork - LGPL
11347  * <script type="text/javascript">
11348  */
11349
11350 /**
11351  * @class Roo.CompositeElementLite
11352  * @extends Roo.CompositeElement
11353  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11354  <pre><code>
11355  var els = Roo.select("#some-el div.some-class");
11356  // or select directly from an existing element
11357  var el = Roo.get('some-el');
11358  el.select('div.some-class');
11359
11360  els.setWidth(100); // all elements become 100 width
11361  els.hide(true); // all elements fade out and hide
11362  // or
11363  els.setWidth(100).hide(true);
11364  </code></pre><br><br>
11365  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11366  * actions will be performed on all the elements in this collection.</b>
11367  */
11368 Roo.CompositeElementLite = function(els){
11369     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11370     this.el = new Roo.Element.Flyweight();
11371 };
11372 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11373     addElements : function(els){
11374         if(els){
11375             if(els instanceof Array){
11376                 this.elements = this.elements.concat(els);
11377             }else{
11378                 var yels = this.elements;
11379                 var index = yels.length-1;
11380                 for(var i = 0, len = els.length; i < len; i++) {
11381                     yels[++index] = els[i];
11382                 }
11383             }
11384         }
11385         return this;
11386     },
11387     invoke : function(fn, args){
11388         var els = this.elements;
11389         var el = this.el;
11390         for(var i = 0, len = els.length; i < len; i++) {
11391             el.dom = els[i];
11392                 Roo.Element.prototype[fn].apply(el, args);
11393         }
11394         return this;
11395     },
11396     /**
11397      * Returns a flyweight Element of the dom element object at the specified index
11398      * @param {Number} index
11399      * @return {Roo.Element}
11400      */
11401     item : function(index){
11402         if(!this.elements[index]){
11403             return null;
11404         }
11405         this.el.dom = this.elements[index];
11406         return this.el;
11407     },
11408
11409     // fixes scope with flyweight
11410     addListener : function(eventName, handler, scope, opt){
11411         var els = this.elements;
11412         for(var i = 0, len = els.length; i < len; i++) {
11413             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11414         }
11415         return this;
11416     },
11417
11418     /**
11419     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11420     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11421     * a reference to the dom node, use el.dom.</b>
11422     * @param {Function} fn The function to call
11423     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11424     * @return {CompositeElement} this
11425     */
11426     each : function(fn, scope){
11427         var els = this.elements;
11428         var el = this.el;
11429         for(var i = 0, len = els.length; i < len; i++){
11430             el.dom = els[i];
11431                 if(fn.call(scope || el, el, this, i) === false){
11432                 break;
11433             }
11434         }
11435         return this;
11436     },
11437
11438     indexOf : function(el){
11439         return this.elements.indexOf(Roo.getDom(el));
11440     },
11441
11442     replaceElement : function(el, replacement, domReplace){
11443         var index = typeof el == 'number' ? el : this.indexOf(el);
11444         if(index !== -1){
11445             replacement = Roo.getDom(replacement);
11446             if(domReplace){
11447                 var d = this.elements[index];
11448                 d.parentNode.insertBefore(replacement, d);
11449                 d.parentNode.removeChild(d);
11450             }
11451             this.elements.splice(index, 1, replacement);
11452         }
11453         return this;
11454     }
11455 });
11456 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11457
11458 /*
11459  * Based on:
11460  * Ext JS Library 1.1.1
11461  * Copyright(c) 2006-2007, Ext JS, LLC.
11462  *
11463  * Originally Released Under LGPL - original licence link has changed is not relivant.
11464  *
11465  * Fork - LGPL
11466  * <script type="text/javascript">
11467  */
11468
11469  
11470
11471 /**
11472  * @class Roo.data.Connection
11473  * @extends Roo.util.Observable
11474  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11475  * either to a configured URL, or to a URL specified at request time. 
11476  * 
11477  * Requests made by this class are asynchronous, and will return immediately. No data from
11478  * the server will be available to the statement immediately following the {@link #request} call.
11479  * To process returned data, use a callback in the request options object, or an event listener.
11480  * 
11481  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11482  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11483  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11484  * property and, if present, the IFRAME's XML document as the responseXML property.
11485  * 
11486  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11487  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11488  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11489  * standard DOM methods.
11490  * @constructor
11491  * @param {Object} config a configuration object.
11492  */
11493 Roo.data.Connection = function(config){
11494     Roo.apply(this, config);
11495     this.addEvents({
11496         /**
11497          * @event beforerequest
11498          * Fires before a network request is made to retrieve a data object.
11499          * @param {Connection} conn This Connection object.
11500          * @param {Object} options The options config object passed to the {@link #request} method.
11501          */
11502         "beforerequest" : true,
11503         /**
11504          * @event requestcomplete
11505          * Fires if the request was successfully completed.
11506          * @param {Connection} conn This Connection object.
11507          * @param {Object} response The XHR object containing the response data.
11508          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11509          * @param {Object} options The options config object passed to the {@link #request} method.
11510          */
11511         "requestcomplete" : true,
11512         /**
11513          * @event requestexception
11514          * Fires if an error HTTP status was returned from the server.
11515          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11516          * @param {Connection} conn This Connection object.
11517          * @param {Object} response The XHR object containing the response data.
11518          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11519          * @param {Object} options The options config object passed to the {@link #request} method.
11520          */
11521         "requestexception" : true
11522     });
11523     Roo.data.Connection.superclass.constructor.call(this);
11524 };
11525
11526 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11527     /**
11528      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11529      */
11530     /**
11531      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11532      * extra parameters to each request made by this object. (defaults to undefined)
11533      */
11534     /**
11535      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11536      *  to each request made by this object. (defaults to undefined)
11537      */
11538     /**
11539      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11540      */
11541     /**
11542      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11543      */
11544     timeout : 30000,
11545     /**
11546      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11547      * @type Boolean
11548      */
11549     autoAbort:false,
11550
11551     /**
11552      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11553      * @type Boolean
11554      */
11555     disableCaching: true,
11556
11557     /**
11558      * Sends an HTTP request to a remote server.
11559      * @param {Object} options An object which may contain the following properties:<ul>
11560      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11561      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11562      * request, a url encoded string or a function to call to get either.</li>
11563      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11564      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11565      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11566      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11567      * <li>options {Object} The parameter to the request call.</li>
11568      * <li>success {Boolean} True if the request succeeded.</li>
11569      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11570      * </ul></li>
11571      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11572      * The callback is passed the following parameters:<ul>
11573      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11574      * <li>options {Object} The parameter to the request call.</li>
11575      * </ul></li>
11576      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11577      * The callback is passed the following parameters:<ul>
11578      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11579      * <li>options {Object} The parameter to the request call.</li>
11580      * </ul></li>
11581      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11582      * for the callback function. Defaults to the browser window.</li>
11583      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11584      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11585      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11586      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11587      * params for the post data. Any params will be appended to the URL.</li>
11588      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11589      * </ul>
11590      * @return {Number} transactionId
11591      */
11592     request : function(o){
11593         if(this.fireEvent("beforerequest", this, o) !== false){
11594             var p = o.params;
11595
11596             if(typeof p == "function"){
11597                 p = p.call(o.scope||window, o);
11598             }
11599             if(typeof p == "object"){
11600                 p = Roo.urlEncode(o.params);
11601             }
11602             if(this.extraParams){
11603                 var extras = Roo.urlEncode(this.extraParams);
11604                 p = p ? (p + '&' + extras) : extras;
11605             }
11606
11607             var url = o.url || this.url;
11608             if(typeof url == 'function'){
11609                 url = url.call(o.scope||window, o);
11610             }
11611
11612             if(o.form){
11613                 var form = Roo.getDom(o.form);
11614                 url = url || form.action;
11615
11616                 var enctype = form.getAttribute("enctype");
11617                 
11618                 if (o.formData) {
11619                     return this.doFormDataUpload(o,p,url);
11620                 }
11621                 
11622                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11623                     return this.doFormUpload(o, p, url);
11624                 }
11625                 var f = Roo.lib.Ajax.serializeForm(form);
11626                 p = p ? (p + '&' + f) : f;
11627             }
11628
11629             var hs = o.headers;
11630             if(this.defaultHeaders){
11631                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11632                 if(!o.headers){
11633                     o.headers = hs;
11634                 }
11635             }
11636
11637             var cb = {
11638                 success: this.handleResponse,
11639                 failure: this.handleFailure,
11640                 scope: this,
11641                 argument: {options: o},
11642                 timeout : o.timeout || this.timeout
11643             };
11644
11645             var method = o.method||this.method||(p ? "POST" : "GET");
11646
11647             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11648                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11649             }
11650
11651             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11652                 if(o.autoAbort){
11653                     this.abort();
11654                 }
11655             }else if(this.autoAbort !== false){
11656                 this.abort();
11657             }
11658
11659             if((method == 'GET' && p) || o.xmlData){
11660                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11661                 p = '';
11662             }
11663             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11664             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11665             Roo.lib.Ajax.useDefaultHeader == true;
11666             return this.transId;
11667         }else{
11668             Roo.callback(o.callback, o.scope, [o, null, null]);
11669             return null;
11670         }
11671     },
11672
11673     /**
11674      * Determine whether this object has a request outstanding.
11675      * @param {Number} transactionId (Optional) defaults to the last transaction
11676      * @return {Boolean} True if there is an outstanding request.
11677      */
11678     isLoading : function(transId){
11679         if(transId){
11680             return Roo.lib.Ajax.isCallInProgress(transId);
11681         }else{
11682             return this.transId ? true : false;
11683         }
11684     },
11685
11686     /**
11687      * Aborts any outstanding request.
11688      * @param {Number} transactionId (Optional) defaults to the last transaction
11689      */
11690     abort : function(transId){
11691         if(transId || this.isLoading()){
11692             Roo.lib.Ajax.abort(transId || this.transId);
11693         }
11694     },
11695
11696     // private
11697     handleResponse : function(response){
11698         this.transId = false;
11699         var options = response.argument.options;
11700         response.argument = options ? options.argument : null;
11701         this.fireEvent("requestcomplete", this, response, options);
11702         Roo.callback(options.success, options.scope, [response, options]);
11703         Roo.callback(options.callback, options.scope, [options, true, response]);
11704     },
11705
11706     // private
11707     handleFailure : function(response, e){
11708         this.transId = false;
11709         var options = response.argument.options;
11710         response.argument = options ? options.argument : null;
11711         this.fireEvent("requestexception", this, response, options, e);
11712         Roo.callback(options.failure, options.scope, [response, options]);
11713         Roo.callback(options.callback, options.scope, [options, false, response]);
11714     },
11715
11716     // private
11717     doFormUpload : function(o, ps, url){
11718         var id = Roo.id();
11719         var frame = document.createElement('iframe');
11720         frame.id = id;
11721         frame.name = id;
11722         frame.className = 'x-hidden';
11723         if(Roo.isIE){
11724             frame.src = Roo.SSL_SECURE_URL;
11725         }
11726         document.body.appendChild(frame);
11727
11728         if(Roo.isIE){
11729            document.frames[id].name = id;
11730         }
11731
11732         var form = Roo.getDom(o.form);
11733         form.target = id;
11734         form.method = 'POST';
11735         form.enctype = form.encoding = 'multipart/form-data';
11736         if(url){
11737             form.action = url;
11738         }
11739
11740         var hiddens, hd;
11741         if(ps){ // add dynamic params
11742             hiddens = [];
11743             ps = Roo.urlDecode(ps, false);
11744             for(var k in ps){
11745                 if(ps.hasOwnProperty(k)){
11746                     hd = document.createElement('input');
11747                     hd.type = 'hidden';
11748                     hd.name = k;
11749                     hd.value = ps[k];
11750                     form.appendChild(hd);
11751                     hiddens.push(hd);
11752                 }
11753             }
11754         }
11755
11756         function cb(){
11757             var r = {  // bogus response object
11758                 responseText : '',
11759                 responseXML : null
11760             };
11761
11762             r.argument = o ? o.argument : null;
11763
11764             try { //
11765                 var doc;
11766                 if(Roo.isIE){
11767                     doc = frame.contentWindow.document;
11768                 }else {
11769                     doc = (frame.contentDocument || window.frames[id].document);
11770                 }
11771                 if(doc && doc.body){
11772                     r.responseText = doc.body.innerHTML;
11773                 }
11774                 if(doc && doc.XMLDocument){
11775                     r.responseXML = doc.XMLDocument;
11776                 }else {
11777                     r.responseXML = doc;
11778                 }
11779             }
11780             catch(e) {
11781                 // ignore
11782             }
11783
11784             Roo.EventManager.removeListener(frame, 'load', cb, this);
11785
11786             this.fireEvent("requestcomplete", this, r, o);
11787             Roo.callback(o.success, o.scope, [r, o]);
11788             Roo.callback(o.callback, o.scope, [o, true, r]);
11789
11790             setTimeout(function(){document.body.removeChild(frame);}, 100);
11791         }
11792
11793         Roo.EventManager.on(frame, 'load', cb, this);
11794         form.submit();
11795
11796         if(hiddens){ // remove dynamic params
11797             for(var i = 0, len = hiddens.length; i < len; i++){
11798                 form.removeChild(hiddens[i]);
11799             }
11800         }
11801     },
11802     // this is a 'formdata version???'
11803     
11804     
11805     doFormDataUpload : function(o, ps, url)
11806     {
11807         var form = Roo.getDom(o.form);
11808         form.enctype = form.encoding = 'multipart/form-data';
11809         var formData = o.formData === true ? new FormData(form) : o.formData;
11810       
11811         var cb = {
11812             success: this.handleResponse,
11813             failure: this.handleFailure,
11814             scope: this,
11815             argument: {options: o},
11816             timeout : o.timeout || this.timeout
11817         };
11818  
11819         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11820             if(o.autoAbort){
11821                 this.abort();
11822             }
11823         }else if(this.autoAbort !== false){
11824             this.abort();
11825         }
11826
11827         //Roo.lib.Ajax.defaultPostHeader = null;
11828         Roo.lib.Ajax.useDefaultHeader = false;
11829         this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
11830         Roo.lib.Ajax.useDefaultHeader = true;
11831  
11832          
11833     }
11834     
11835 });
11836 /*
11837  * Based on:
11838  * Ext JS Library 1.1.1
11839  * Copyright(c) 2006-2007, Ext JS, LLC.
11840  *
11841  * Originally Released Under LGPL - original licence link has changed is not relivant.
11842  *
11843  * Fork - LGPL
11844  * <script type="text/javascript">
11845  */
11846  
11847 /**
11848  * Global Ajax request class.
11849  * 
11850  * @class Roo.Ajax
11851  * @extends Roo.data.Connection
11852  * @static
11853  * 
11854  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11855  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11856  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11857  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11858  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11859  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11860  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11861  */
11862 Roo.Ajax = new Roo.data.Connection({
11863     // fix up the docs
11864     /**
11865      * @scope Roo.Ajax
11866      * @type {Boolear} 
11867      */
11868     autoAbort : false,
11869
11870     /**
11871      * Serialize the passed form into a url encoded string
11872      * @scope Roo.Ajax
11873      * @param {String/HTMLElement} form
11874      * @return {String}
11875      */
11876     serializeForm : function(form){
11877         return Roo.lib.Ajax.serializeForm(form);
11878     }
11879 });/*
11880  * Based on:
11881  * Ext JS Library 1.1.1
11882  * Copyright(c) 2006-2007, Ext JS, LLC.
11883  *
11884  * Originally Released Under LGPL - original licence link has changed is not relivant.
11885  *
11886  * Fork - LGPL
11887  * <script type="text/javascript">
11888  */
11889
11890  
11891 /**
11892  * @class Roo.UpdateManager
11893  * @extends Roo.util.Observable
11894  * Provides AJAX-style update for Element object.<br><br>
11895  * Usage:<br>
11896  * <pre><code>
11897  * // Get it from a Roo.Element object
11898  * var el = Roo.get("foo");
11899  * var mgr = el.getUpdateManager();
11900  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11901  * ...
11902  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11903  * <br>
11904  * // or directly (returns the same UpdateManager instance)
11905  * var mgr = new Roo.UpdateManager("myElementId");
11906  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11907  * mgr.on("update", myFcnNeedsToKnow);
11908  * <br>
11909    // short handed call directly from the element object
11910    Roo.get("foo").load({
11911         url: "bar.php",
11912         scripts:true,
11913         params: "for=bar",
11914         text: "Loading Foo..."
11915    });
11916  * </code></pre>
11917  * @constructor
11918  * Create new UpdateManager directly.
11919  * @param {String/HTMLElement/Roo.Element} el The element to update
11920  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11921  */
11922 Roo.UpdateManager = function(el, forceNew){
11923     el = Roo.get(el);
11924     if(!forceNew && el.updateManager){
11925         return el.updateManager;
11926     }
11927     /**
11928      * The Element object
11929      * @type Roo.Element
11930      */
11931     this.el = el;
11932     /**
11933      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11934      * @type String
11935      */
11936     this.defaultUrl = null;
11937
11938     this.addEvents({
11939         /**
11940          * @event beforeupdate
11941          * Fired before an update is made, return false from your handler and the update is cancelled.
11942          * @param {Roo.Element} el
11943          * @param {String/Object/Function} url
11944          * @param {String/Object} params
11945          */
11946         "beforeupdate": true,
11947         /**
11948          * @event update
11949          * Fired after successful update is made.
11950          * @param {Roo.Element} el
11951          * @param {Object} oResponseObject The response Object
11952          */
11953         "update": true,
11954         /**
11955          * @event failure
11956          * Fired on update failure.
11957          * @param {Roo.Element} el
11958          * @param {Object} oResponseObject The response Object
11959          */
11960         "failure": true
11961     });
11962     var d = Roo.UpdateManager.defaults;
11963     /**
11964      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11965      * @type String
11966      */
11967     this.sslBlankUrl = d.sslBlankUrl;
11968     /**
11969      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11970      * @type Boolean
11971      */
11972     this.disableCaching = d.disableCaching;
11973     /**
11974      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11975      * @type String
11976      */
11977     this.indicatorText = d.indicatorText;
11978     /**
11979      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11980      * @type String
11981      */
11982     this.showLoadIndicator = d.showLoadIndicator;
11983     /**
11984      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11985      * @type Number
11986      */
11987     this.timeout = d.timeout;
11988
11989     /**
11990      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11991      * @type Boolean
11992      */
11993     this.loadScripts = d.loadScripts;
11994
11995     /**
11996      * Transaction object of current executing transaction
11997      */
11998     this.transaction = null;
11999
12000     /**
12001      * @private
12002      */
12003     this.autoRefreshProcId = null;
12004     /**
12005      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12006      * @type Function
12007      */
12008     this.refreshDelegate = this.refresh.createDelegate(this);
12009     /**
12010      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12011      * @type Function
12012      */
12013     this.updateDelegate = this.update.createDelegate(this);
12014     /**
12015      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12016      * @type Function
12017      */
12018     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12019     /**
12020      * @private
12021      */
12022     this.successDelegate = this.processSuccess.createDelegate(this);
12023     /**
12024      * @private
12025      */
12026     this.failureDelegate = this.processFailure.createDelegate(this);
12027
12028     if(!this.renderer){
12029      /**
12030       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12031       */
12032     this.renderer = new Roo.UpdateManager.BasicRenderer();
12033     }
12034     
12035     Roo.UpdateManager.superclass.constructor.call(this);
12036 };
12037
12038 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12039     /**
12040      * Get the Element this UpdateManager is bound to
12041      * @return {Roo.Element} The element
12042      */
12043     getEl : function(){
12044         return this.el;
12045     },
12046     /**
12047      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12048      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
12049 <pre><code>
12050 um.update({<br/>
12051     url: "your-url.php",<br/>
12052     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12053     callback: yourFunction,<br/>
12054     scope: yourObject, //(optional scope)  <br/>
12055     discardUrl: false, <br/>
12056     nocache: false,<br/>
12057     text: "Loading...",<br/>
12058     timeout: 30,<br/>
12059     scripts: false<br/>
12060 });
12061 </code></pre>
12062      * The only required property is url. The optional properties nocache, text and scripts
12063      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12064      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
12065      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12066      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
12067      */
12068     update : function(url, params, callback, discardUrl){
12069         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12070             var method = this.method,
12071                 cfg;
12072             if(typeof url == "object"){ // must be config object
12073                 cfg = url;
12074                 url = cfg.url;
12075                 params = params || cfg.params;
12076                 callback = callback || cfg.callback;
12077                 discardUrl = discardUrl || cfg.discardUrl;
12078                 if(callback && cfg.scope){
12079                     callback = callback.createDelegate(cfg.scope);
12080                 }
12081                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12082                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12083                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12084                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12085                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12086             }
12087             this.showLoading();
12088             if(!discardUrl){
12089                 this.defaultUrl = url;
12090             }
12091             if(typeof url == "function"){
12092                 url = url.call(this);
12093             }
12094
12095             method = method || (params ? "POST" : "GET");
12096             if(method == "GET"){
12097                 url = this.prepareUrl(url);
12098             }
12099
12100             var o = Roo.apply(cfg ||{}, {
12101                 url : url,
12102                 params: params,
12103                 success: this.successDelegate,
12104                 failure: this.failureDelegate,
12105                 callback: undefined,
12106                 timeout: (this.timeout*1000),
12107                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12108             });
12109             Roo.log("updated manager called with timeout of " + o.timeout);
12110             this.transaction = Roo.Ajax.request(o);
12111         }
12112     },
12113
12114     /**
12115      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
12116      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12117      * @param {String/HTMLElement} form The form Id or form element
12118      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12119      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12120      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12121      */
12122     formUpdate : function(form, url, reset, callback){
12123         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12124             if(typeof url == "function"){
12125                 url = url.call(this);
12126             }
12127             form = Roo.getDom(form);
12128             this.transaction = Roo.Ajax.request({
12129                 form: form,
12130                 url:url,
12131                 success: this.successDelegate,
12132                 failure: this.failureDelegate,
12133                 timeout: (this.timeout*1000),
12134                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12135             });
12136             this.showLoading.defer(1, this);
12137         }
12138     },
12139
12140     /**
12141      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12142      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12143      */
12144     refresh : function(callback){
12145         if(this.defaultUrl == null){
12146             return;
12147         }
12148         this.update(this.defaultUrl, null, callback, true);
12149     },
12150
12151     /**
12152      * Set this element to auto refresh.
12153      * @param {Number} interval How often to update (in seconds).
12154      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
12155      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
12156      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12157      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12158      */
12159     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12160         if(refreshNow){
12161             this.update(url || this.defaultUrl, params, callback, true);
12162         }
12163         if(this.autoRefreshProcId){
12164             clearInterval(this.autoRefreshProcId);
12165         }
12166         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12167     },
12168
12169     /**
12170      * Stop auto refresh on this element.
12171      */
12172      stopAutoRefresh : function(){
12173         if(this.autoRefreshProcId){
12174             clearInterval(this.autoRefreshProcId);
12175             delete this.autoRefreshProcId;
12176         }
12177     },
12178
12179     isAutoRefreshing : function(){
12180        return this.autoRefreshProcId ? true : false;
12181     },
12182     /**
12183      * Called to update the element to "Loading" state. Override to perform custom action.
12184      */
12185     showLoading : function(){
12186         if(this.showLoadIndicator){
12187             this.el.update(this.indicatorText);
12188         }
12189     },
12190
12191     /**
12192      * Adds unique parameter to query string if disableCaching = true
12193      * @private
12194      */
12195     prepareUrl : function(url){
12196         if(this.disableCaching){
12197             var append = "_dc=" + (new Date().getTime());
12198             if(url.indexOf("?") !== -1){
12199                 url += "&" + append;
12200             }else{
12201                 url += "?" + append;
12202             }
12203         }
12204         return url;
12205     },
12206
12207     /**
12208      * @private
12209      */
12210     processSuccess : function(response){
12211         this.transaction = null;
12212         if(response.argument.form && response.argument.reset){
12213             try{ // put in try/catch since some older FF releases had problems with this
12214                 response.argument.form.reset();
12215             }catch(e){}
12216         }
12217         if(this.loadScripts){
12218             this.renderer.render(this.el, response, this,
12219                 this.updateComplete.createDelegate(this, [response]));
12220         }else{
12221             this.renderer.render(this.el, response, this);
12222             this.updateComplete(response);
12223         }
12224     },
12225
12226     updateComplete : function(response){
12227         this.fireEvent("update", this.el, response);
12228         if(typeof response.argument.callback == "function"){
12229             response.argument.callback(this.el, true, response);
12230         }
12231     },
12232
12233     /**
12234      * @private
12235      */
12236     processFailure : function(response){
12237         this.transaction = null;
12238         this.fireEvent("failure", this.el, response);
12239         if(typeof response.argument.callback == "function"){
12240             response.argument.callback(this.el, false, response);
12241         }
12242     },
12243
12244     /**
12245      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12246      * @param {Object} renderer The object implementing the render() method
12247      */
12248     setRenderer : function(renderer){
12249         this.renderer = renderer;
12250     },
12251
12252     getRenderer : function(){
12253        return this.renderer;
12254     },
12255
12256     /**
12257      * Set the defaultUrl used for updates
12258      * @param {String/Function} defaultUrl The url or a function to call to get the url
12259      */
12260     setDefaultUrl : function(defaultUrl){
12261         this.defaultUrl = defaultUrl;
12262     },
12263
12264     /**
12265      * Aborts the executing transaction
12266      */
12267     abort : function(){
12268         if(this.transaction){
12269             Roo.Ajax.abort(this.transaction);
12270         }
12271     },
12272
12273     /**
12274      * Returns true if an update is in progress
12275      * @return {Boolean}
12276      */
12277     isUpdating : function(){
12278         if(this.transaction){
12279             return Roo.Ajax.isLoading(this.transaction);
12280         }
12281         return false;
12282     }
12283 });
12284
12285 /**
12286  * @class Roo.UpdateManager.defaults
12287  * @static (not really - but it helps the doc tool)
12288  * The defaults collection enables customizing the default properties of UpdateManager
12289  */
12290    Roo.UpdateManager.defaults = {
12291        /**
12292          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12293          * @type Number
12294          */
12295          timeout : 30,
12296
12297          /**
12298          * True to process scripts by default (Defaults to false).
12299          * @type Boolean
12300          */
12301         loadScripts : false,
12302
12303         /**
12304         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12305         * @type String
12306         */
12307         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12308         /**
12309          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12310          * @type Boolean
12311          */
12312         disableCaching : false,
12313         /**
12314          * Whether to show indicatorText when loading (Defaults to true).
12315          * @type Boolean
12316          */
12317         showLoadIndicator : true,
12318         /**
12319          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12320          * @type String
12321          */
12322         indicatorText : '<div class="loading-indicator">Loading...</div>'
12323    };
12324
12325 /**
12326  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12327  *Usage:
12328  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12329  * @param {String/HTMLElement/Roo.Element} el The element to update
12330  * @param {String} url The url
12331  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12332  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12333  * @static
12334  * @deprecated
12335  * @member Roo.UpdateManager
12336  */
12337 Roo.UpdateManager.updateElement = function(el, url, params, options){
12338     var um = Roo.get(el, true).getUpdateManager();
12339     Roo.apply(um, options);
12340     um.update(url, params, options ? options.callback : null);
12341 };
12342 // alias for backwards compat
12343 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12344 /**
12345  * @class Roo.UpdateManager.BasicRenderer
12346  * Default Content renderer. Updates the elements innerHTML with the responseText.
12347  */
12348 Roo.UpdateManager.BasicRenderer = function(){};
12349
12350 Roo.UpdateManager.BasicRenderer.prototype = {
12351     /**
12352      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12353      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12354      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12355      * @param {Roo.Element} el The element being rendered
12356      * @param {Object} response The YUI Connect response object
12357      * @param {UpdateManager} updateManager The calling update manager
12358      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12359      */
12360      render : function(el, response, updateManager, callback){
12361         el.update(response.responseText, updateManager.loadScripts, callback);
12362     }
12363 };
12364 /*
12365  * Based on:
12366  * Roo JS
12367  * (c)) Alan Knowles
12368  * Licence : LGPL
12369  */
12370
12371
12372 /**
12373  * @class Roo.DomTemplate
12374  * @extends Roo.Template
12375  * An effort at a dom based template engine..
12376  *
12377  * Similar to XTemplate, except it uses dom parsing to create the template..
12378  *
12379  * Supported features:
12380  *
12381  *  Tags:
12382
12383 <pre><code>
12384       {a_variable} - output encoded.
12385       {a_variable.format:("Y-m-d")} - call a method on the variable
12386       {a_variable:raw} - unencoded output
12387       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12388       {a_variable:this.method_on_template(...)} - call a method on the template object.
12389  
12390 </code></pre>
12391  *  The tpl tag:
12392 <pre><code>
12393         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12394         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12395         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12396         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12397   
12398 </code></pre>
12399  *      
12400  */
12401 Roo.DomTemplate = function()
12402 {
12403      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12404      if (this.html) {
12405         this.compile();
12406      }
12407 };
12408
12409
12410 Roo.extend(Roo.DomTemplate, Roo.Template, {
12411     /**
12412      * id counter for sub templates.
12413      */
12414     id : 0,
12415     /**
12416      * flag to indicate if dom parser is inside a pre,
12417      * it will strip whitespace if not.
12418      */
12419     inPre : false,
12420     
12421     /**
12422      * The various sub templates
12423      */
12424     tpls : false,
12425     
12426     
12427     
12428     /**
12429      *
12430      * basic tag replacing syntax
12431      * WORD:WORD()
12432      *
12433      * // you can fake an object call by doing this
12434      *  x.t:(test,tesT) 
12435      * 
12436      */
12437     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12438     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12439     
12440     iterChild : function (node, method) {
12441         
12442         var oldPre = this.inPre;
12443         if (node.tagName == 'PRE') {
12444             this.inPre = true;
12445         }
12446         for( var i = 0; i < node.childNodes.length; i++) {
12447             method.call(this, node.childNodes[i]);
12448         }
12449         this.inPre = oldPre;
12450     },
12451     
12452     
12453     
12454     /**
12455      * compile the template
12456      *
12457      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12458      *
12459      */
12460     compile: function()
12461     {
12462         var s = this.html;
12463         
12464         // covert the html into DOM...
12465         var doc = false;
12466         var div =false;
12467         try {
12468             doc = document.implementation.createHTMLDocument("");
12469             doc.documentElement.innerHTML =   this.html  ;
12470             div = doc.documentElement;
12471         } catch (e) {
12472             // old IE... - nasty -- it causes all sorts of issues.. with
12473             // images getting pulled from server..
12474             div = document.createElement('div');
12475             div.innerHTML = this.html;
12476         }
12477         //doc.documentElement.innerHTML = htmlBody
12478          
12479         
12480         
12481         this.tpls = [];
12482         var _t = this;
12483         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12484         
12485         var tpls = this.tpls;
12486         
12487         // create a top level template from the snippet..
12488         
12489         //Roo.log(div.innerHTML);
12490         
12491         var tpl = {
12492             uid : 'master',
12493             id : this.id++,
12494             attr : false,
12495             value : false,
12496             body : div.innerHTML,
12497             
12498             forCall : false,
12499             execCall : false,
12500             dom : div,
12501             isTop : true
12502             
12503         };
12504         tpls.unshift(tpl);
12505         
12506         
12507         // compile them...
12508         this.tpls = [];
12509         Roo.each(tpls, function(tp){
12510             this.compileTpl(tp);
12511             this.tpls[tp.id] = tp;
12512         }, this);
12513         
12514         this.master = tpls[0];
12515         return this;
12516         
12517         
12518     },
12519     
12520     compileNode : function(node, istop) {
12521         // test for
12522         //Roo.log(node);
12523         
12524         
12525         // skip anything not a tag..
12526         if (node.nodeType != 1) {
12527             if (node.nodeType == 3 && !this.inPre) {
12528                 // reduce white space..
12529                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12530                 
12531             }
12532             return;
12533         }
12534         
12535         var tpl = {
12536             uid : false,
12537             id : false,
12538             attr : false,
12539             value : false,
12540             body : '',
12541             
12542             forCall : false,
12543             execCall : false,
12544             dom : false,
12545             isTop : istop
12546             
12547             
12548         };
12549         
12550         
12551         switch(true) {
12552             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12553             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12554             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12555             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12556             // no default..
12557         }
12558         
12559         
12560         if (!tpl.attr) {
12561             // just itterate children..
12562             this.iterChild(node,this.compileNode);
12563             return;
12564         }
12565         tpl.uid = this.id++;
12566         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12567         node.removeAttribute('roo-'+ tpl.attr);
12568         if (tpl.attr != 'name') {
12569             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12570             node.parentNode.replaceChild(placeholder,  node);
12571         } else {
12572             
12573             var placeholder =  document.createElement('span');
12574             placeholder.className = 'roo-tpl-' + tpl.value;
12575             node.parentNode.replaceChild(placeholder,  node);
12576         }
12577         
12578         // parent now sees '{domtplXXXX}
12579         this.iterChild(node,this.compileNode);
12580         
12581         // we should now have node body...
12582         var div = document.createElement('div');
12583         div.appendChild(node);
12584         tpl.dom = node;
12585         // this has the unfortunate side effect of converting tagged attributes
12586         // eg. href="{...}" into %7C...%7D
12587         // this has been fixed by searching for those combo's although it's a bit hacky..
12588         
12589         
12590         tpl.body = div.innerHTML;
12591         
12592         
12593          
12594         tpl.id = tpl.uid;
12595         switch(tpl.attr) {
12596             case 'for' :
12597                 switch (tpl.value) {
12598                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12599                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12600                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12601                 }
12602                 break;
12603             
12604             case 'exec':
12605                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12606                 break;
12607             
12608             case 'if':     
12609                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12610                 break;
12611             
12612             case 'name':
12613                 tpl.id  = tpl.value; // replace non characters???
12614                 break;
12615             
12616         }
12617         
12618         
12619         this.tpls.push(tpl);
12620         
12621         
12622         
12623     },
12624     
12625     
12626     
12627     
12628     /**
12629      * Compile a segment of the template into a 'sub-template'
12630      *
12631      * 
12632      * 
12633      *
12634      */
12635     compileTpl : function(tpl)
12636     {
12637         var fm = Roo.util.Format;
12638         var useF = this.disableFormats !== true;
12639         
12640         var sep = Roo.isGecko ? "+\n" : ",\n";
12641         
12642         var undef = function(str) {
12643             Roo.debug && Roo.log("Property not found :"  + str);
12644             return '';
12645         };
12646           
12647         //Roo.log(tpl.body);
12648         
12649         
12650         
12651         var fn = function(m, lbrace, name, format, args)
12652         {
12653             //Roo.log("ARGS");
12654             //Roo.log(arguments);
12655             args = args ? args.replace(/\\'/g,"'") : args;
12656             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12657             if (typeof(format) == 'undefined') {
12658                 format =  'htmlEncode'; 
12659             }
12660             if (format == 'raw' ) {
12661                 format = false;
12662             }
12663             
12664             if(name.substr(0, 6) == 'domtpl'){
12665                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12666             }
12667             
12668             // build an array of options to determine if value is undefined..
12669             
12670             // basically get 'xxxx.yyyy' then do
12671             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12672             //    (function () { Roo.log("Property not found"); return ''; })() :
12673             //    ......
12674             
12675             var udef_ar = [];
12676             var lookfor = '';
12677             Roo.each(name.split('.'), function(st) {
12678                 lookfor += (lookfor.length ? '.': '') + st;
12679                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12680             });
12681             
12682             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12683             
12684             
12685             if(format && useF){
12686                 
12687                 args = args ? ',' + args : "";
12688                  
12689                 if(format.substr(0, 5) != "this."){
12690                     format = "fm." + format + '(';
12691                 }else{
12692                     format = 'this.call("'+ format.substr(5) + '", ';
12693                     args = ", values";
12694                 }
12695                 
12696                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12697             }
12698              
12699             if (args && args.length) {
12700                 // called with xxyx.yuu:(test,test)
12701                 // change to ()
12702                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12703             }
12704             // raw.. - :raw modifier..
12705             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12706             
12707         };
12708         var body;
12709         // branched to use + in gecko and [].join() in others
12710         if(Roo.isGecko){
12711             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12712                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12713                     "';};};";
12714         }else{
12715             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12716             body.push(tpl.body.replace(/(\r\n|\n)/g,
12717                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12718             body.push("'].join('');};};");
12719             body = body.join('');
12720         }
12721         
12722         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12723        
12724         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12725         eval(body);
12726         
12727         return this;
12728     },
12729      
12730     /**
12731      * same as applyTemplate, except it's done to one of the subTemplates
12732      * when using named templates, you can do:
12733      *
12734      * var str = pl.applySubTemplate('your-name', values);
12735      *
12736      * 
12737      * @param {Number} id of the template
12738      * @param {Object} values to apply to template
12739      * @param {Object} parent (normaly the instance of this object)
12740      */
12741     applySubTemplate : function(id, values, parent)
12742     {
12743         
12744         
12745         var t = this.tpls[id];
12746         
12747         
12748         try { 
12749             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12750                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12751                 return '';
12752             }
12753         } catch(e) {
12754             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12755             Roo.log(values);
12756           
12757             return '';
12758         }
12759         try { 
12760             
12761             if(t.execCall && t.execCall.call(this, values, parent)){
12762                 return '';
12763             }
12764         } catch(e) {
12765             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12766             Roo.log(values);
12767             return '';
12768         }
12769         
12770         try {
12771             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12772             parent = t.target ? values : parent;
12773             if(t.forCall && vs instanceof Array){
12774                 var buf = [];
12775                 for(var i = 0, len = vs.length; i < len; i++){
12776                     try {
12777                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12778                     } catch (e) {
12779                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12780                         Roo.log(e.body);
12781                         //Roo.log(t.compiled);
12782                         Roo.log(vs[i]);
12783                     }   
12784                 }
12785                 return buf.join('');
12786             }
12787         } catch (e) {
12788             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12789             Roo.log(values);
12790             return '';
12791         }
12792         try {
12793             return t.compiled.call(this, vs, parent);
12794         } catch (e) {
12795             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12796             Roo.log(e.body);
12797             //Roo.log(t.compiled);
12798             Roo.log(values);
12799             return '';
12800         }
12801     },
12802
12803    
12804
12805     applyTemplate : function(values){
12806         return this.master.compiled.call(this, values, {});
12807         //var s = this.subs;
12808     },
12809
12810     apply : function(){
12811         return this.applyTemplate.apply(this, arguments);
12812     }
12813
12814  });
12815
12816 Roo.DomTemplate.from = function(el){
12817     el = Roo.getDom(el);
12818     return new Roo.Domtemplate(el.value || el.innerHTML);
12819 };/*
12820  * Based on:
12821  * Ext JS Library 1.1.1
12822  * Copyright(c) 2006-2007, Ext JS, LLC.
12823  *
12824  * Originally Released Under LGPL - original licence link has changed is not relivant.
12825  *
12826  * Fork - LGPL
12827  * <script type="text/javascript">
12828  */
12829
12830 /**
12831  * @class Roo.util.DelayedTask
12832  * Provides a convenient method of performing setTimeout where a new
12833  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12834  * You can use this class to buffer
12835  * the keypress events for a certain number of milliseconds, and perform only if they stop
12836  * for that amount of time.
12837  * @constructor The parameters to this constructor serve as defaults and are not required.
12838  * @param {Function} fn (optional) The default function to timeout
12839  * @param {Object} scope (optional) The default scope of that timeout
12840  * @param {Array} args (optional) The default Array of arguments
12841  */
12842 Roo.util.DelayedTask = function(fn, scope, args){
12843     var id = null, d, t;
12844
12845     var call = function(){
12846         var now = new Date().getTime();
12847         if(now - t >= d){
12848             clearInterval(id);
12849             id = null;
12850             fn.apply(scope, args || []);
12851         }
12852     };
12853     /**
12854      * Cancels any pending timeout and queues a new one
12855      * @param {Number} delay The milliseconds to delay
12856      * @param {Function} newFn (optional) Overrides function passed to constructor
12857      * @param {Object} newScope (optional) Overrides scope passed to constructor
12858      * @param {Array} newArgs (optional) Overrides args passed to constructor
12859      */
12860     this.delay = function(delay, newFn, newScope, newArgs){
12861         if(id && delay != d){
12862             this.cancel();
12863         }
12864         d = delay;
12865         t = new Date().getTime();
12866         fn = newFn || fn;
12867         scope = newScope || scope;
12868         args = newArgs || args;
12869         if(!id){
12870             id = setInterval(call, d);
12871         }
12872     };
12873
12874     /**
12875      * Cancel the last queued timeout
12876      */
12877     this.cancel = function(){
12878         if(id){
12879             clearInterval(id);
12880             id = null;
12881         }
12882     };
12883 };/*
12884  * Based on:
12885  * Ext JS Library 1.1.1
12886  * Copyright(c) 2006-2007, Ext JS, LLC.
12887  *
12888  * Originally Released Under LGPL - original licence link has changed is not relivant.
12889  *
12890  * Fork - LGPL
12891  * <script type="text/javascript">
12892  */
12893  
12894  
12895 Roo.util.TaskRunner = function(interval){
12896     interval = interval || 10;
12897     var tasks = [], removeQueue = [];
12898     var id = 0;
12899     var running = false;
12900
12901     var stopThread = function(){
12902         running = false;
12903         clearInterval(id);
12904         id = 0;
12905     };
12906
12907     var startThread = function(){
12908         if(!running){
12909             running = true;
12910             id = setInterval(runTasks, interval);
12911         }
12912     };
12913
12914     var removeTask = function(task){
12915         removeQueue.push(task);
12916         if(task.onStop){
12917             task.onStop();
12918         }
12919     };
12920
12921     var runTasks = function(){
12922         if(removeQueue.length > 0){
12923             for(var i = 0, len = removeQueue.length; i < len; i++){
12924                 tasks.remove(removeQueue[i]);
12925             }
12926             removeQueue = [];
12927             if(tasks.length < 1){
12928                 stopThread();
12929                 return;
12930             }
12931         }
12932         var now = new Date().getTime();
12933         for(var i = 0, len = tasks.length; i < len; ++i){
12934             var t = tasks[i];
12935             var itime = now - t.taskRunTime;
12936             if(t.interval <= itime){
12937                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12938                 t.taskRunTime = now;
12939                 if(rt === false || t.taskRunCount === t.repeat){
12940                     removeTask(t);
12941                     return;
12942                 }
12943             }
12944             if(t.duration && t.duration <= (now - t.taskStartTime)){
12945                 removeTask(t);
12946             }
12947         }
12948     };
12949
12950     /**
12951      * Queues a new task.
12952      * @param {Object} task
12953      */
12954     this.start = function(task){
12955         tasks.push(task);
12956         task.taskStartTime = new Date().getTime();
12957         task.taskRunTime = 0;
12958         task.taskRunCount = 0;
12959         startThread();
12960         return task;
12961     };
12962
12963     this.stop = function(task){
12964         removeTask(task);
12965         return task;
12966     };
12967
12968     this.stopAll = function(){
12969         stopThread();
12970         for(var i = 0, len = tasks.length; i < len; i++){
12971             if(tasks[i].onStop){
12972                 tasks[i].onStop();
12973             }
12974         }
12975         tasks = [];
12976         removeQueue = [];
12977     };
12978 };
12979
12980 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12981  * Based on:
12982  * Ext JS Library 1.1.1
12983  * Copyright(c) 2006-2007, Ext JS, LLC.
12984  *
12985  * Originally Released Under LGPL - original licence link has changed is not relivant.
12986  *
12987  * Fork - LGPL
12988  * <script type="text/javascript">
12989  */
12990
12991  
12992 /**
12993  * @class Roo.util.MixedCollection
12994  * @extends Roo.util.Observable
12995  * A Collection class that maintains both numeric indexes and keys and exposes events.
12996  * @constructor
12997  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12998  * collection (defaults to false)
12999  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13000  * and return the key value for that item.  This is used when available to look up the key on items that
13001  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13002  * equivalent to providing an implementation for the {@link #getKey} method.
13003  */
13004 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13005     this.items = [];
13006     this.map = {};
13007     this.keys = [];
13008     this.length = 0;
13009     this.addEvents({
13010         /**
13011          * @event clear
13012          * Fires when the collection is cleared.
13013          */
13014         "clear" : true,
13015         /**
13016          * @event add
13017          * Fires when an item is added to the collection.
13018          * @param {Number} index The index at which the item was added.
13019          * @param {Object} o The item added.
13020          * @param {String} key The key associated with the added item.
13021          */
13022         "add" : true,
13023         /**
13024          * @event replace
13025          * Fires when an item is replaced in the collection.
13026          * @param {String} key he key associated with the new added.
13027          * @param {Object} old The item being replaced.
13028          * @param {Object} new The new item.
13029          */
13030         "replace" : true,
13031         /**
13032          * @event remove
13033          * Fires when an item is removed from the collection.
13034          * @param {Object} o The item being removed.
13035          * @param {String} key (optional) The key associated with the removed item.
13036          */
13037         "remove" : true,
13038         "sort" : true
13039     });
13040     this.allowFunctions = allowFunctions === true;
13041     if(keyFn){
13042         this.getKey = keyFn;
13043     }
13044     Roo.util.MixedCollection.superclass.constructor.call(this);
13045 };
13046
13047 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13048     allowFunctions : false,
13049     
13050 /**
13051  * Adds an item to the collection.
13052  * @param {String} key The key to associate with the item
13053  * @param {Object} o The item to add.
13054  * @return {Object} The item added.
13055  */
13056     add : function(key, o){
13057         if(arguments.length == 1){
13058             o = arguments[0];
13059             key = this.getKey(o);
13060         }
13061         if(typeof key == "undefined" || key === null){
13062             this.length++;
13063             this.items.push(o);
13064             this.keys.push(null);
13065         }else{
13066             var old = this.map[key];
13067             if(old){
13068                 return this.replace(key, o);
13069             }
13070             this.length++;
13071             this.items.push(o);
13072             this.map[key] = o;
13073             this.keys.push(key);
13074         }
13075         this.fireEvent("add", this.length-1, o, key);
13076         return o;
13077     },
13078        
13079 /**
13080   * MixedCollection has a generic way to fetch keys if you implement getKey.
13081 <pre><code>
13082 // normal way
13083 var mc = new Roo.util.MixedCollection();
13084 mc.add(someEl.dom.id, someEl);
13085 mc.add(otherEl.dom.id, otherEl);
13086 //and so on
13087
13088 // using getKey
13089 var mc = new Roo.util.MixedCollection();
13090 mc.getKey = function(el){
13091    return el.dom.id;
13092 };
13093 mc.add(someEl);
13094 mc.add(otherEl);
13095
13096 // or via the constructor
13097 var mc = new Roo.util.MixedCollection(false, function(el){
13098    return el.dom.id;
13099 });
13100 mc.add(someEl);
13101 mc.add(otherEl);
13102 </code></pre>
13103  * @param o {Object} The item for which to find the key.
13104  * @return {Object} The key for the passed item.
13105  */
13106     getKey : function(o){
13107          return o.id; 
13108     },
13109    
13110 /**
13111  * Replaces an item in the collection.
13112  * @param {String} key The key associated with the item to replace, or the item to replace.
13113  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13114  * @return {Object}  The new item.
13115  */
13116     replace : function(key, o){
13117         if(arguments.length == 1){
13118             o = arguments[0];
13119             key = this.getKey(o);
13120         }
13121         var old = this.item(key);
13122         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13123              return this.add(key, o);
13124         }
13125         var index = this.indexOfKey(key);
13126         this.items[index] = o;
13127         this.map[key] = o;
13128         this.fireEvent("replace", key, old, o);
13129         return o;
13130     },
13131    
13132 /**
13133  * Adds all elements of an Array or an Object to the collection.
13134  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13135  * an Array of values, each of which are added to the collection.
13136  */
13137     addAll : function(objs){
13138         if(arguments.length > 1 || objs instanceof Array){
13139             var args = arguments.length > 1 ? arguments : objs;
13140             for(var i = 0, len = args.length; i < len; i++){
13141                 this.add(args[i]);
13142             }
13143         }else{
13144             for(var key in objs){
13145                 if(this.allowFunctions || typeof objs[key] != "function"){
13146                     this.add(key, objs[key]);
13147                 }
13148             }
13149         }
13150     },
13151    
13152 /**
13153  * Executes the specified function once for every item in the collection, passing each
13154  * item as the first and only parameter. returning false from the function will stop the iteration.
13155  * @param {Function} fn The function to execute for each item.
13156  * @param {Object} scope (optional) The scope in which to execute the function.
13157  */
13158     each : function(fn, scope){
13159         var items = [].concat(this.items); // each safe for removal
13160         for(var i = 0, len = items.length; i < len; i++){
13161             if(fn.call(scope || items[i], items[i], i, len) === false){
13162                 break;
13163             }
13164         }
13165     },
13166    
13167 /**
13168  * Executes the specified function once for every key in the collection, passing each
13169  * key, and its associated item as the first two parameters.
13170  * @param {Function} fn The function to execute for each item.
13171  * @param {Object} scope (optional) The scope in which to execute the function.
13172  */
13173     eachKey : function(fn, scope){
13174         for(var i = 0, len = this.keys.length; i < len; i++){
13175             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13176         }
13177     },
13178    
13179 /**
13180  * Returns the first item in the collection which elicits a true return value from the
13181  * passed selection function.
13182  * @param {Function} fn The selection function to execute for each item.
13183  * @param {Object} scope (optional) The scope in which to execute the function.
13184  * @return {Object} The first item in the collection which returned true from the selection function.
13185  */
13186     find : function(fn, scope){
13187         for(var i = 0, len = this.items.length; i < len; i++){
13188             if(fn.call(scope || window, this.items[i], this.keys[i])){
13189                 return this.items[i];
13190             }
13191         }
13192         return null;
13193     },
13194    
13195 /**
13196  * Inserts an item at the specified index in the collection.
13197  * @param {Number} index The index to insert the item at.
13198  * @param {String} key The key to associate with the new item, or the item itself.
13199  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13200  * @return {Object} The item inserted.
13201  */
13202     insert : function(index, key, o){
13203         if(arguments.length == 2){
13204             o = arguments[1];
13205             key = this.getKey(o);
13206         }
13207         if(index >= this.length){
13208             return this.add(key, o);
13209         }
13210         this.length++;
13211         this.items.splice(index, 0, o);
13212         if(typeof key != "undefined" && key != null){
13213             this.map[key] = o;
13214         }
13215         this.keys.splice(index, 0, key);
13216         this.fireEvent("add", index, o, key);
13217         return o;
13218     },
13219    
13220 /**
13221  * Removed an item from the collection.
13222  * @param {Object} o The item to remove.
13223  * @return {Object} The item removed.
13224  */
13225     remove : function(o){
13226         return this.removeAt(this.indexOf(o));
13227     },
13228    
13229 /**
13230  * Remove an item from a specified index in the collection.
13231  * @param {Number} index The index within the collection of the item to remove.
13232  */
13233     removeAt : function(index){
13234         if(index < this.length && index >= 0){
13235             this.length--;
13236             var o = this.items[index];
13237             this.items.splice(index, 1);
13238             var key = this.keys[index];
13239             if(typeof key != "undefined"){
13240                 delete this.map[key];
13241             }
13242             this.keys.splice(index, 1);
13243             this.fireEvent("remove", o, key);
13244         }
13245     },
13246    
13247 /**
13248  * Removed an item associated with the passed key fom the collection.
13249  * @param {String} key The key of the item to remove.
13250  */
13251     removeKey : function(key){
13252         return this.removeAt(this.indexOfKey(key));
13253     },
13254    
13255 /**
13256  * Returns the number of items in the collection.
13257  * @return {Number} the number of items in the collection.
13258  */
13259     getCount : function(){
13260         return this.length; 
13261     },
13262    
13263 /**
13264  * Returns index within the collection of the passed Object.
13265  * @param {Object} o The item to find the index of.
13266  * @return {Number} index of the item.
13267  */
13268     indexOf : function(o){
13269         if(!this.items.indexOf){
13270             for(var i = 0, len = this.items.length; i < len; i++){
13271                 if(this.items[i] == o) {
13272                     return i;
13273                 }
13274             }
13275             return -1;
13276         }else{
13277             return this.items.indexOf(o);
13278         }
13279     },
13280    
13281 /**
13282  * Returns index within the collection of the passed key.
13283  * @param {String} key The key to find the index of.
13284  * @return {Number} index of the key.
13285  */
13286     indexOfKey : function(key){
13287         if(!this.keys.indexOf){
13288             for(var i = 0, len = this.keys.length; i < len; i++){
13289                 if(this.keys[i] == key) {
13290                     return i;
13291                 }
13292             }
13293             return -1;
13294         }else{
13295             return this.keys.indexOf(key);
13296         }
13297     },
13298    
13299 /**
13300  * Returns the item associated with the passed key OR index. Key has priority over index.
13301  * @param {String/Number} key The key or index of the item.
13302  * @return {Object} The item associated with the passed key.
13303  */
13304     item : function(key){
13305         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13306         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13307     },
13308     
13309 /**
13310  * Returns the item at the specified index.
13311  * @param {Number} index The index of the item.
13312  * @return {Object}
13313  */
13314     itemAt : function(index){
13315         return this.items[index];
13316     },
13317     
13318 /**
13319  * Returns the item associated with the passed key.
13320  * @param {String/Number} key The key of the item.
13321  * @return {Object} The item associated with the passed key.
13322  */
13323     key : function(key){
13324         return this.map[key];
13325     },
13326    
13327 /**
13328  * Returns true if the collection contains the passed Object as an item.
13329  * @param {Object} o  The Object to look for in the collection.
13330  * @return {Boolean} True if the collection contains the Object as an item.
13331  */
13332     contains : function(o){
13333         return this.indexOf(o) != -1;
13334     },
13335    
13336 /**
13337  * Returns true if the collection contains the passed Object as a key.
13338  * @param {String} key The key to look for in the collection.
13339  * @return {Boolean} True if the collection contains the Object as a key.
13340  */
13341     containsKey : function(key){
13342         return typeof this.map[key] != "undefined";
13343     },
13344    
13345 /**
13346  * Removes all items from the collection.
13347  */
13348     clear : function(){
13349         this.length = 0;
13350         this.items = [];
13351         this.keys = [];
13352         this.map = {};
13353         this.fireEvent("clear");
13354     },
13355    
13356 /**
13357  * Returns the first item in the collection.
13358  * @return {Object} the first item in the collection..
13359  */
13360     first : function(){
13361         return this.items[0]; 
13362     },
13363    
13364 /**
13365  * Returns the last item in the collection.
13366  * @return {Object} the last item in the collection..
13367  */
13368     last : function(){
13369         return this.items[this.length-1];   
13370     },
13371     
13372     _sort : function(property, dir, fn){
13373         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13374         fn = fn || function(a, b){
13375             return a-b;
13376         };
13377         var c = [], k = this.keys, items = this.items;
13378         for(var i = 0, len = items.length; i < len; i++){
13379             c[c.length] = {key: k[i], value: items[i], index: i};
13380         }
13381         c.sort(function(a, b){
13382             var v = fn(a[property], b[property]) * dsc;
13383             if(v == 0){
13384                 v = (a.index < b.index ? -1 : 1);
13385             }
13386             return v;
13387         });
13388         for(var i = 0, len = c.length; i < len; i++){
13389             items[i] = c[i].value;
13390             k[i] = c[i].key;
13391         }
13392         this.fireEvent("sort", this);
13393     },
13394     
13395     /**
13396      * Sorts this collection with the passed comparison function
13397      * @param {String} direction (optional) "ASC" or "DESC"
13398      * @param {Function} fn (optional) comparison function
13399      */
13400     sort : function(dir, fn){
13401         this._sort("value", dir, fn);
13402     },
13403     
13404     /**
13405      * Sorts this collection by keys
13406      * @param {String} direction (optional) "ASC" or "DESC"
13407      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13408      */
13409     keySort : function(dir, fn){
13410         this._sort("key", dir, fn || function(a, b){
13411             return String(a).toUpperCase()-String(b).toUpperCase();
13412         });
13413     },
13414     
13415     /**
13416      * Returns a range of items in this collection
13417      * @param {Number} startIndex (optional) defaults to 0
13418      * @param {Number} endIndex (optional) default to the last item
13419      * @return {Array} An array of items
13420      */
13421     getRange : function(start, end){
13422         var items = this.items;
13423         if(items.length < 1){
13424             return [];
13425         }
13426         start = start || 0;
13427         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13428         var r = [];
13429         if(start <= end){
13430             for(var i = start; i <= end; i++) {
13431                     r[r.length] = items[i];
13432             }
13433         }else{
13434             for(var i = start; i >= end; i--) {
13435                     r[r.length] = items[i];
13436             }
13437         }
13438         return r;
13439     },
13440         
13441     /**
13442      * Filter the <i>objects</i> in this collection by a specific property. 
13443      * Returns a new collection that has been filtered.
13444      * @param {String} property A property on your objects
13445      * @param {String/RegExp} value Either string that the property values 
13446      * should start with or a RegExp to test against the property
13447      * @return {MixedCollection} The new filtered collection
13448      */
13449     filter : function(property, value){
13450         if(!value.exec){ // not a regex
13451             value = String(value);
13452             if(value.length == 0){
13453                 return this.clone();
13454             }
13455             value = new RegExp("^" + Roo.escapeRe(value), "i");
13456         }
13457         return this.filterBy(function(o){
13458             return o && value.test(o[property]);
13459         });
13460         },
13461     
13462     /**
13463      * Filter by a function. * Returns a new collection that has been filtered.
13464      * The passed function will be called with each 
13465      * object in the collection. If the function returns true, the value is included 
13466      * otherwise it is filtered.
13467      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13468      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13469      * @return {MixedCollection} The new filtered collection
13470      */
13471     filterBy : function(fn, scope){
13472         var r = new Roo.util.MixedCollection();
13473         r.getKey = this.getKey;
13474         var k = this.keys, it = this.items;
13475         for(var i = 0, len = it.length; i < len; i++){
13476             if(fn.call(scope||this, it[i], k[i])){
13477                                 r.add(k[i], it[i]);
13478                         }
13479         }
13480         return r;
13481     },
13482     
13483     /**
13484      * Creates a duplicate of this collection
13485      * @return {MixedCollection}
13486      */
13487     clone : function(){
13488         var r = new Roo.util.MixedCollection();
13489         var k = this.keys, it = this.items;
13490         for(var i = 0, len = it.length; i < len; i++){
13491             r.add(k[i], it[i]);
13492         }
13493         r.getKey = this.getKey;
13494         return r;
13495     }
13496 });
13497 /**
13498  * Returns the item associated with the passed key or index.
13499  * @method
13500  * @param {String/Number} key The key or index of the item.
13501  * @return {Object} The item associated with the passed key.
13502  */
13503 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13504  * Based on:
13505  * Ext JS Library 1.1.1
13506  * Copyright(c) 2006-2007, Ext JS, LLC.
13507  *
13508  * Originally Released Under LGPL - original licence link has changed is not relivant.
13509  *
13510  * Fork - LGPL
13511  * <script type="text/javascript">
13512  */
13513 /**
13514  * @class Roo.util.JSON
13515  * Modified version of Douglas Crockford"s json.js that doesn"t
13516  * mess with the Object prototype 
13517  * http://www.json.org/js.html
13518  * @singleton
13519  */
13520 Roo.util.JSON = new (function(){
13521     var useHasOwn = {}.hasOwnProperty ? true : false;
13522     
13523     // crashes Safari in some instances
13524     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13525     
13526     var pad = function(n) {
13527         return n < 10 ? "0" + n : n;
13528     };
13529     
13530     var m = {
13531         "\b": '\\b',
13532         "\t": '\\t',
13533         "\n": '\\n',
13534         "\f": '\\f',
13535         "\r": '\\r',
13536         '"' : '\\"',
13537         "\\": '\\\\'
13538     };
13539
13540     var encodeString = function(s){
13541         if (/["\\\x00-\x1f]/.test(s)) {
13542             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13543                 var c = m[b];
13544                 if(c){
13545                     return c;
13546                 }
13547                 c = b.charCodeAt();
13548                 return "\\u00" +
13549                     Math.floor(c / 16).toString(16) +
13550                     (c % 16).toString(16);
13551             }) + '"';
13552         }
13553         return '"' + s + '"';
13554     };
13555     
13556     var encodeArray = function(o){
13557         var a = ["["], b, i, l = o.length, v;
13558             for (i = 0; i < l; i += 1) {
13559                 v = o[i];
13560                 switch (typeof v) {
13561                     case "undefined":
13562                     case "function":
13563                     case "unknown":
13564                         break;
13565                     default:
13566                         if (b) {
13567                             a.push(',');
13568                         }
13569                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13570                         b = true;
13571                 }
13572             }
13573             a.push("]");
13574             return a.join("");
13575     };
13576     
13577     var encodeDate = function(o){
13578         return '"' + o.getFullYear() + "-" +
13579                 pad(o.getMonth() + 1) + "-" +
13580                 pad(o.getDate()) + "T" +
13581                 pad(o.getHours()) + ":" +
13582                 pad(o.getMinutes()) + ":" +
13583                 pad(o.getSeconds()) + '"';
13584     };
13585     
13586     /**
13587      * Encodes an Object, Array or other value
13588      * @param {Mixed} o The variable to encode
13589      * @return {String} The JSON string
13590      */
13591     this.encode = function(o)
13592     {
13593         // should this be extended to fully wrap stringify..
13594         
13595         if(typeof o == "undefined" || o === null){
13596             return "null";
13597         }else if(o instanceof Array){
13598             return encodeArray(o);
13599         }else if(o instanceof Date){
13600             return encodeDate(o);
13601         }else if(typeof o == "string"){
13602             return encodeString(o);
13603         }else if(typeof o == "number"){
13604             return isFinite(o) ? String(o) : "null";
13605         }else if(typeof o == "boolean"){
13606             return String(o);
13607         }else {
13608             var a = ["{"], b, i, v;
13609             for (i in o) {
13610                 if(!useHasOwn || o.hasOwnProperty(i)) {
13611                     v = o[i];
13612                     switch (typeof v) {
13613                     case "undefined":
13614                     case "function":
13615                     case "unknown":
13616                         break;
13617                     default:
13618                         if(b){
13619                             a.push(',');
13620                         }
13621                         a.push(this.encode(i), ":",
13622                                 v === null ? "null" : this.encode(v));
13623                         b = true;
13624                     }
13625                 }
13626             }
13627             a.push("}");
13628             return a.join("");
13629         }
13630     };
13631     
13632     /**
13633      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13634      * @param {String} json The JSON string
13635      * @return {Object} The resulting object
13636      */
13637     this.decode = function(json){
13638         
13639         return  /** eval:var:json */ eval("(" + json + ')');
13640     };
13641 })();
13642 /** 
13643  * Shorthand for {@link Roo.util.JSON#encode}
13644  * @member Roo encode 
13645  * @method */
13646 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13647 /** 
13648  * Shorthand for {@link Roo.util.JSON#decode}
13649  * @member Roo decode 
13650  * @method */
13651 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13652 /*
13653  * Based on:
13654  * Ext JS Library 1.1.1
13655  * Copyright(c) 2006-2007, Ext JS, LLC.
13656  *
13657  * Originally Released Under LGPL - original licence link has changed is not relivant.
13658  *
13659  * Fork - LGPL
13660  * <script type="text/javascript">
13661  */
13662  
13663 /**
13664  * @class Roo.util.Format
13665  * Reusable data formatting functions
13666  * @singleton
13667  */
13668 Roo.util.Format = function(){
13669     var trimRe = /^\s+|\s+$/g;
13670     return {
13671         /**
13672          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13673          * @param {String} value The string to truncate
13674          * @param {Number} length The maximum length to allow before truncating
13675          * @return {String} The converted text
13676          */
13677         ellipsis : function(value, len){
13678             if(value && value.length > len){
13679                 return value.substr(0, len-3)+"...";
13680             }
13681             return value;
13682         },
13683
13684         /**
13685          * Checks a reference and converts it to empty string if it is undefined
13686          * @param {Mixed} value Reference to check
13687          * @return {Mixed} Empty string if converted, otherwise the original value
13688          */
13689         undef : function(value){
13690             return typeof value != "undefined" ? value : "";
13691         },
13692
13693         /**
13694          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13695          * @param {String} value The string to encode
13696          * @return {String} The encoded text
13697          */
13698         htmlEncode : function(value){
13699             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13700         },
13701
13702         /**
13703          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13704          * @param {String} value The string to decode
13705          * @return {String} The decoded text
13706          */
13707         htmlDecode : function(value){
13708             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13709         },
13710
13711         /**
13712          * Trims any whitespace from either side of a string
13713          * @param {String} value The text to trim
13714          * @return {String} The trimmed text
13715          */
13716         trim : function(value){
13717             return String(value).replace(trimRe, "");
13718         },
13719
13720         /**
13721          * Returns a substring from within an original string
13722          * @param {String} value The original text
13723          * @param {Number} start The start index of the substring
13724          * @param {Number} length The length of the substring
13725          * @return {String} The substring
13726          */
13727         substr : function(value, start, length){
13728             return String(value).substr(start, length);
13729         },
13730
13731         /**
13732          * Converts a string to all lower case letters
13733          * @param {String} value The text to convert
13734          * @return {String} The converted text
13735          */
13736         lowercase : function(value){
13737             return String(value).toLowerCase();
13738         },
13739
13740         /**
13741          * Converts a string to all upper case letters
13742          * @param {String} value The text to convert
13743          * @return {String} The converted text
13744          */
13745         uppercase : function(value){
13746             return String(value).toUpperCase();
13747         },
13748
13749         /**
13750          * Converts the first character only of a string to upper case
13751          * @param {String} value The text to convert
13752          * @return {String} The converted text
13753          */
13754         capitalize : function(value){
13755             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13756         },
13757
13758         // private
13759         call : function(value, fn){
13760             if(arguments.length > 2){
13761                 var args = Array.prototype.slice.call(arguments, 2);
13762                 args.unshift(value);
13763                  
13764                 return /** eval:var:value */  eval(fn).apply(window, args);
13765             }else{
13766                 /** eval:var:value */
13767                 return /** eval:var:value */ eval(fn).call(window, value);
13768             }
13769         },
13770
13771        
13772         /**
13773          * safer version of Math.toFixed..??/
13774          * @param {Number/String} value The numeric value to format
13775          * @param {Number/String} value Decimal places 
13776          * @return {String} The formatted currency string
13777          */
13778         toFixed : function(v, n)
13779         {
13780             // why not use to fixed - precision is buggered???
13781             if (!n) {
13782                 return Math.round(v-0);
13783             }
13784             var fact = Math.pow(10,n+1);
13785             v = (Math.round((v-0)*fact))/fact;
13786             var z = (''+fact).substring(2);
13787             if (v == Math.floor(v)) {
13788                 return Math.floor(v) + '.' + z;
13789             }
13790             
13791             // now just padd decimals..
13792             var ps = String(v).split('.');
13793             var fd = (ps[1] + z);
13794             var r = fd.substring(0,n); 
13795             var rm = fd.substring(n); 
13796             if (rm < 5) {
13797                 return ps[0] + '.' + r;
13798             }
13799             r*=1; // turn it into a number;
13800             r++;
13801             if (String(r).length != n) {
13802                 ps[0]*=1;
13803                 ps[0]++;
13804                 r = String(r).substring(1); // chop the end off.
13805             }
13806             
13807             return ps[0] + '.' + r;
13808              
13809         },
13810         
13811         /**
13812          * Format a number as US currency
13813          * @param {Number/String} value The numeric value to format
13814          * @return {String} The formatted currency string
13815          */
13816         usMoney : function(v){
13817             return '$' + Roo.util.Format.number(v);
13818         },
13819         
13820         /**
13821          * Format a number
13822          * eventually this should probably emulate php's number_format
13823          * @param {Number/String} value The numeric value to format
13824          * @param {Number} decimals number of decimal places
13825          * @param {String} delimiter for thousands (default comma)
13826          * @return {String} The formatted currency string
13827          */
13828         number : function(v, decimals, thousandsDelimiter)
13829         {
13830             // multiply and round.
13831             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13832             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13833             
13834             var mul = Math.pow(10, decimals);
13835             var zero = String(mul).substring(1);
13836             v = (Math.round((v-0)*mul))/mul;
13837             
13838             // if it's '0' number.. then
13839             
13840             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13841             v = String(v);
13842             var ps = v.split('.');
13843             var whole = ps[0];
13844             
13845             var r = /(\d+)(\d{3})/;
13846             // add comma's
13847             
13848             if(thousandsDelimiter.length != 0) {
13849                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13850             } 
13851             
13852             var sub = ps[1] ?
13853                     // has decimals..
13854                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13855                     // does not have decimals
13856                     (decimals ? ('.' + zero) : '');
13857             
13858             
13859             return whole + sub ;
13860         },
13861         
13862         /**
13863          * Parse a value into a formatted date using the specified format pattern.
13864          * @param {Mixed} value The value to format
13865          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13866          * @return {String} The formatted date string
13867          */
13868         date : function(v, format){
13869             if(!v){
13870                 return "";
13871             }
13872             if(!(v instanceof Date)){
13873                 v = new Date(Date.parse(v));
13874             }
13875             return v.dateFormat(format || Roo.util.Format.defaults.date);
13876         },
13877
13878         /**
13879          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13880          * @param {String} format Any valid date format string
13881          * @return {Function} The date formatting function
13882          */
13883         dateRenderer : function(format){
13884             return function(v){
13885                 return Roo.util.Format.date(v, format);  
13886             };
13887         },
13888
13889         // private
13890         stripTagsRE : /<\/?[^>]+>/gi,
13891         
13892         /**
13893          * Strips all HTML tags
13894          * @param {Mixed} value The text from which to strip tags
13895          * @return {String} The stripped text
13896          */
13897         stripTags : function(v){
13898             return !v ? v : String(v).replace(this.stripTagsRE, "");
13899         }
13900     };
13901 }();
13902 Roo.util.Format.defaults = {
13903     date : 'd/M/Y'
13904 };/*
13905  * Based on:
13906  * Ext JS Library 1.1.1
13907  * Copyright(c) 2006-2007, Ext JS, LLC.
13908  *
13909  * Originally Released Under LGPL - original licence link has changed is not relivant.
13910  *
13911  * Fork - LGPL
13912  * <script type="text/javascript">
13913  */
13914
13915
13916  
13917
13918 /**
13919  * @class Roo.MasterTemplate
13920  * @extends Roo.Template
13921  * Provides a template that can have child templates. The syntax is:
13922 <pre><code>
13923 var t = new Roo.MasterTemplate(
13924         '&lt;select name="{name}"&gt;',
13925                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13926         '&lt;/select&gt;'
13927 );
13928 t.add('options', {value: 'foo', text: 'bar'});
13929 // or you can add multiple child elements in one shot
13930 t.addAll('options', [
13931     {value: 'foo', text: 'bar'},
13932     {value: 'foo2', text: 'bar2'},
13933     {value: 'foo3', text: 'bar3'}
13934 ]);
13935 // then append, applying the master template values
13936 t.append('my-form', {name: 'my-select'});
13937 </code></pre>
13938 * A name attribute for the child template is not required if you have only one child
13939 * template or you want to refer to them by index.
13940  */
13941 Roo.MasterTemplate = function(){
13942     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13943     this.originalHtml = this.html;
13944     var st = {};
13945     var m, re = this.subTemplateRe;
13946     re.lastIndex = 0;
13947     var subIndex = 0;
13948     while(m = re.exec(this.html)){
13949         var name = m[1], content = m[2];
13950         st[subIndex] = {
13951             name: name,
13952             index: subIndex,
13953             buffer: [],
13954             tpl : new Roo.Template(content)
13955         };
13956         if(name){
13957             st[name] = st[subIndex];
13958         }
13959         st[subIndex].tpl.compile();
13960         st[subIndex].tpl.call = this.call.createDelegate(this);
13961         subIndex++;
13962     }
13963     this.subCount = subIndex;
13964     this.subs = st;
13965 };
13966 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13967     /**
13968     * The regular expression used to match sub templates
13969     * @type RegExp
13970     * @property
13971     */
13972     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13973
13974     /**
13975      * Applies the passed values to a child template.
13976      * @param {String/Number} name (optional) The name or index of the child template
13977      * @param {Array/Object} values The values to be applied to the template
13978      * @return {MasterTemplate} this
13979      */
13980      add : function(name, values){
13981         if(arguments.length == 1){
13982             values = arguments[0];
13983             name = 0;
13984         }
13985         var s = this.subs[name];
13986         s.buffer[s.buffer.length] = s.tpl.apply(values);
13987         return this;
13988     },
13989
13990     /**
13991      * Applies all the passed values to a child template.
13992      * @param {String/Number} name (optional) The name or index of the child template
13993      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13994      * @param {Boolean} reset (optional) True to reset the template first
13995      * @return {MasterTemplate} this
13996      */
13997     fill : function(name, values, reset){
13998         var a = arguments;
13999         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14000             values = a[0];
14001             name = 0;
14002             reset = a[1];
14003         }
14004         if(reset){
14005             this.reset();
14006         }
14007         for(var i = 0, len = values.length; i < len; i++){
14008             this.add(name, values[i]);
14009         }
14010         return this;
14011     },
14012
14013     /**
14014      * Resets the template for reuse
14015      * @return {MasterTemplate} this
14016      */
14017      reset : function(){
14018         var s = this.subs;
14019         for(var i = 0; i < this.subCount; i++){
14020             s[i].buffer = [];
14021         }
14022         return this;
14023     },
14024
14025     applyTemplate : function(values){
14026         var s = this.subs;
14027         var replaceIndex = -1;
14028         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14029             return s[++replaceIndex].buffer.join("");
14030         });
14031         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14032     },
14033
14034     apply : function(){
14035         return this.applyTemplate.apply(this, arguments);
14036     },
14037
14038     compile : function(){return this;}
14039 });
14040
14041 /**
14042  * Alias for fill().
14043  * @method
14044  */
14045 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14046  /**
14047  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14048  * var tpl = Roo.MasterTemplate.from('element-id');
14049  * @param {String/HTMLElement} el
14050  * @param {Object} config
14051  * @static
14052  */
14053 Roo.MasterTemplate.from = function(el, config){
14054     el = Roo.getDom(el);
14055     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14056 };/*
14057  * Based on:
14058  * Ext JS Library 1.1.1
14059  * Copyright(c) 2006-2007, Ext JS, LLC.
14060  *
14061  * Originally Released Under LGPL - original licence link has changed is not relivant.
14062  *
14063  * Fork - LGPL
14064  * <script type="text/javascript">
14065  */
14066
14067  
14068 /**
14069  * @class Roo.util.CSS
14070  * Utility class for manipulating CSS rules
14071  * @singleton
14072  */
14073 Roo.util.CSS = function(){
14074         var rules = null;
14075         var doc = document;
14076
14077     var camelRe = /(-[a-z])/gi;
14078     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14079
14080    return {
14081    /**
14082     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14083     * tag and appended to the HEAD of the document.
14084     * @param {String|Object} cssText The text containing the css rules
14085     * @param {String} id An id to add to the stylesheet for later removal
14086     * @return {StyleSheet}
14087     */
14088     createStyleSheet : function(cssText, id){
14089         var ss;
14090         var head = doc.getElementsByTagName("head")[0];
14091         var nrules = doc.createElement("style");
14092         nrules.setAttribute("type", "text/css");
14093         if(id){
14094             nrules.setAttribute("id", id);
14095         }
14096         if (typeof(cssText) != 'string') {
14097             // support object maps..
14098             // not sure if this a good idea.. 
14099             // perhaps it should be merged with the general css handling
14100             // and handle js style props.
14101             var cssTextNew = [];
14102             for(var n in cssText) {
14103                 var citems = [];
14104                 for(var k in cssText[n]) {
14105                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14106                 }
14107                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14108                 
14109             }
14110             cssText = cssTextNew.join("\n");
14111             
14112         }
14113        
14114        
14115        if(Roo.isIE){
14116            head.appendChild(nrules);
14117            ss = nrules.styleSheet;
14118            ss.cssText = cssText;
14119        }else{
14120            try{
14121                 nrules.appendChild(doc.createTextNode(cssText));
14122            }catch(e){
14123                nrules.cssText = cssText; 
14124            }
14125            head.appendChild(nrules);
14126            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14127        }
14128        this.cacheStyleSheet(ss);
14129        return ss;
14130    },
14131
14132    /**
14133     * Removes a style or link tag by id
14134     * @param {String} id The id of the tag
14135     */
14136    removeStyleSheet : function(id){
14137        var existing = doc.getElementById(id);
14138        if(existing){
14139            existing.parentNode.removeChild(existing);
14140        }
14141    },
14142
14143    /**
14144     * Dynamically swaps an existing stylesheet reference for a new one
14145     * @param {String} id The id of an existing link tag to remove
14146     * @param {String} url The href of the new stylesheet to include
14147     */
14148    swapStyleSheet : function(id, url){
14149        this.removeStyleSheet(id);
14150        var ss = doc.createElement("link");
14151        ss.setAttribute("rel", "stylesheet");
14152        ss.setAttribute("type", "text/css");
14153        ss.setAttribute("id", id);
14154        ss.setAttribute("href", url);
14155        doc.getElementsByTagName("head")[0].appendChild(ss);
14156    },
14157    
14158    /**
14159     * Refresh the rule cache if you have dynamically added stylesheets
14160     * @return {Object} An object (hash) of rules indexed by selector
14161     */
14162    refreshCache : function(){
14163        return this.getRules(true);
14164    },
14165
14166    // private
14167    cacheStyleSheet : function(stylesheet){
14168        if(!rules){
14169            rules = {};
14170        }
14171        try{// try catch for cross domain access issue
14172            var ssRules = stylesheet.cssRules || stylesheet.rules;
14173            for(var j = ssRules.length-1; j >= 0; --j){
14174                rules[ssRules[j].selectorText] = ssRules[j];
14175            }
14176        }catch(e){}
14177    },
14178    
14179    /**
14180     * Gets all css rules for the document
14181     * @param {Boolean} refreshCache true to refresh the internal cache
14182     * @return {Object} An object (hash) of rules indexed by selector
14183     */
14184    getRules : function(refreshCache){
14185                 if(rules == null || refreshCache){
14186                         rules = {};
14187                         var ds = doc.styleSheets;
14188                         for(var i =0, len = ds.length; i < len; i++){
14189                             try{
14190                         this.cacheStyleSheet(ds[i]);
14191                     }catch(e){} 
14192                 }
14193                 }
14194                 return rules;
14195         },
14196         
14197         /**
14198     * Gets an an individual CSS rule by selector(s)
14199     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14200     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14201     * @return {CSSRule} The CSS rule or null if one is not found
14202     */
14203    getRule : function(selector, refreshCache){
14204                 var rs = this.getRules(refreshCache);
14205                 if(!(selector instanceof Array)){
14206                     return rs[selector];
14207                 }
14208                 for(var i = 0; i < selector.length; i++){
14209                         if(rs[selector[i]]){
14210                                 return rs[selector[i]];
14211                         }
14212                 }
14213                 return null;
14214         },
14215         
14216         
14217         /**
14218     * Updates a rule property
14219     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14220     * @param {String} property The css property
14221     * @param {String} value The new value for the property
14222     * @return {Boolean} true If a rule was found and updated
14223     */
14224    updateRule : function(selector, property, value){
14225                 if(!(selector instanceof Array)){
14226                         var rule = this.getRule(selector);
14227                         if(rule){
14228                                 rule.style[property.replace(camelRe, camelFn)] = value;
14229                                 return true;
14230                         }
14231                 }else{
14232                         for(var i = 0; i < selector.length; i++){
14233                                 if(this.updateRule(selector[i], property, value)){
14234                                         return true;
14235                                 }
14236                         }
14237                 }
14238                 return false;
14239         }
14240    };   
14241 }();/*
14242  * Based on:
14243  * Ext JS Library 1.1.1
14244  * Copyright(c) 2006-2007, Ext JS, LLC.
14245  *
14246  * Originally Released Under LGPL - original licence link has changed is not relivant.
14247  *
14248  * Fork - LGPL
14249  * <script type="text/javascript">
14250  */
14251
14252  
14253
14254 /**
14255  * @class Roo.util.ClickRepeater
14256  * @extends Roo.util.Observable
14257  * 
14258  * A wrapper class which can be applied to any element. Fires a "click" event while the
14259  * mouse is pressed. The interval between firings may be specified in the config but
14260  * defaults to 10 milliseconds.
14261  * 
14262  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14263  * 
14264  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14265  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14266  * Similar to an autorepeat key delay.
14267  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14268  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14269  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14270  *           "interval" and "delay" are ignored. "immediate" is honored.
14271  * @cfg {Boolean} preventDefault True to prevent the default click event
14272  * @cfg {Boolean} stopDefault True to stop the default click event
14273  * 
14274  * @history
14275  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14276  *     2007-02-02 jvs Renamed to ClickRepeater
14277  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14278  *
14279  *  @constructor
14280  * @param {String/HTMLElement/Element} el The element to listen on
14281  * @param {Object} config
14282  **/
14283 Roo.util.ClickRepeater = function(el, config)
14284 {
14285     this.el = Roo.get(el);
14286     this.el.unselectable();
14287
14288     Roo.apply(this, config);
14289
14290     this.addEvents({
14291     /**
14292      * @event mousedown
14293      * Fires when the mouse button is depressed.
14294      * @param {Roo.util.ClickRepeater} this
14295      */
14296         "mousedown" : true,
14297     /**
14298      * @event click
14299      * Fires on a specified interval during the time the element is pressed.
14300      * @param {Roo.util.ClickRepeater} this
14301      */
14302         "click" : true,
14303     /**
14304      * @event mouseup
14305      * Fires when the mouse key is released.
14306      * @param {Roo.util.ClickRepeater} this
14307      */
14308         "mouseup" : true
14309     });
14310
14311     this.el.on("mousedown", this.handleMouseDown, this);
14312     if(this.preventDefault || this.stopDefault){
14313         this.el.on("click", function(e){
14314             if(this.preventDefault){
14315                 e.preventDefault();
14316             }
14317             if(this.stopDefault){
14318                 e.stopEvent();
14319             }
14320         }, this);
14321     }
14322
14323     // allow inline handler
14324     if(this.handler){
14325         this.on("click", this.handler,  this.scope || this);
14326     }
14327
14328     Roo.util.ClickRepeater.superclass.constructor.call(this);
14329 };
14330
14331 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14332     interval : 20,
14333     delay: 250,
14334     preventDefault : true,
14335     stopDefault : false,
14336     timer : 0,
14337
14338     // private
14339     handleMouseDown : function(){
14340         clearTimeout(this.timer);
14341         this.el.blur();
14342         if(this.pressClass){
14343             this.el.addClass(this.pressClass);
14344         }
14345         this.mousedownTime = new Date();
14346
14347         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14348         this.el.on("mouseout", this.handleMouseOut, this);
14349
14350         this.fireEvent("mousedown", this);
14351         this.fireEvent("click", this);
14352         
14353         this.timer = this.click.defer(this.delay || this.interval, this);
14354     },
14355
14356     // private
14357     click : function(){
14358         this.fireEvent("click", this);
14359         this.timer = this.click.defer(this.getInterval(), this);
14360     },
14361
14362     // private
14363     getInterval: function(){
14364         if(!this.accelerate){
14365             return this.interval;
14366         }
14367         var pressTime = this.mousedownTime.getElapsed();
14368         if(pressTime < 500){
14369             return 400;
14370         }else if(pressTime < 1700){
14371             return 320;
14372         }else if(pressTime < 2600){
14373             return 250;
14374         }else if(pressTime < 3500){
14375             return 180;
14376         }else if(pressTime < 4400){
14377             return 140;
14378         }else if(pressTime < 5300){
14379             return 80;
14380         }else if(pressTime < 6200){
14381             return 50;
14382         }else{
14383             return 10;
14384         }
14385     },
14386
14387     // private
14388     handleMouseOut : function(){
14389         clearTimeout(this.timer);
14390         if(this.pressClass){
14391             this.el.removeClass(this.pressClass);
14392         }
14393         this.el.on("mouseover", this.handleMouseReturn, this);
14394     },
14395
14396     // private
14397     handleMouseReturn : function(){
14398         this.el.un("mouseover", this.handleMouseReturn);
14399         if(this.pressClass){
14400             this.el.addClass(this.pressClass);
14401         }
14402         this.click();
14403     },
14404
14405     // private
14406     handleMouseUp : function(){
14407         clearTimeout(this.timer);
14408         this.el.un("mouseover", this.handleMouseReturn);
14409         this.el.un("mouseout", this.handleMouseOut);
14410         Roo.get(document).un("mouseup", this.handleMouseUp);
14411         this.el.removeClass(this.pressClass);
14412         this.fireEvent("mouseup", this);
14413     }
14414 });/*
14415  * Based on:
14416  * Ext JS Library 1.1.1
14417  * Copyright(c) 2006-2007, Ext JS, LLC.
14418  *
14419  * Originally Released Under LGPL - original licence link has changed is not relivant.
14420  *
14421  * Fork - LGPL
14422  * <script type="text/javascript">
14423  */
14424
14425  
14426 /**
14427  * @class Roo.KeyNav
14428  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14429  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14430  * way to implement custom navigation schemes for any UI component.</p>
14431  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14432  * pageUp, pageDown, del, home, end.  Usage:</p>
14433  <pre><code>
14434 var nav = new Roo.KeyNav("my-element", {
14435     "left" : function(e){
14436         this.moveLeft(e.ctrlKey);
14437     },
14438     "right" : function(e){
14439         this.moveRight(e.ctrlKey);
14440     },
14441     "enter" : function(e){
14442         this.save();
14443     },
14444     scope : this
14445 });
14446 </code></pre>
14447  * @constructor
14448  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14449  * @param {Object} config The config
14450  */
14451 Roo.KeyNav = function(el, config){
14452     this.el = Roo.get(el);
14453     Roo.apply(this, config);
14454     if(!this.disabled){
14455         this.disabled = true;
14456         this.enable();
14457     }
14458 };
14459
14460 Roo.KeyNav.prototype = {
14461     /**
14462      * @cfg {Boolean} disabled
14463      * True to disable this KeyNav instance (defaults to false)
14464      */
14465     disabled : false,
14466     /**
14467      * @cfg {String} defaultEventAction
14468      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14469      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14470      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14471      */
14472     defaultEventAction: "stopEvent",
14473     /**
14474      * @cfg {Boolean} forceKeyDown
14475      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14476      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14477      * handle keydown instead of keypress.
14478      */
14479     forceKeyDown : false,
14480
14481     // private
14482     prepareEvent : function(e){
14483         var k = e.getKey();
14484         var h = this.keyToHandler[k];
14485         //if(h && this[h]){
14486         //    e.stopPropagation();
14487         //}
14488         if(Roo.isSafari && h && k >= 37 && k <= 40){
14489             e.stopEvent();
14490         }
14491     },
14492
14493     // private
14494     relay : function(e){
14495         var k = e.getKey();
14496         var h = this.keyToHandler[k];
14497         if(h && this[h]){
14498             if(this.doRelay(e, this[h], h) !== true){
14499                 e[this.defaultEventAction]();
14500             }
14501         }
14502     },
14503
14504     // private
14505     doRelay : function(e, h, hname){
14506         return h.call(this.scope || this, e);
14507     },
14508
14509     // possible handlers
14510     enter : false,
14511     left : false,
14512     right : false,
14513     up : false,
14514     down : false,
14515     tab : false,
14516     esc : false,
14517     pageUp : false,
14518     pageDown : false,
14519     del : false,
14520     home : false,
14521     end : false,
14522
14523     // quick lookup hash
14524     keyToHandler : {
14525         37 : "left",
14526         39 : "right",
14527         38 : "up",
14528         40 : "down",
14529         33 : "pageUp",
14530         34 : "pageDown",
14531         46 : "del",
14532         36 : "home",
14533         35 : "end",
14534         13 : "enter",
14535         27 : "esc",
14536         9  : "tab"
14537     },
14538
14539         /**
14540          * Enable this KeyNav
14541          */
14542         enable: function(){
14543                 if(this.disabled){
14544             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14545             // the EventObject will normalize Safari automatically
14546             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14547                 this.el.on("keydown", this.relay,  this);
14548             }else{
14549                 this.el.on("keydown", this.prepareEvent,  this);
14550                 this.el.on("keypress", this.relay,  this);
14551             }
14552                     this.disabled = false;
14553                 }
14554         },
14555
14556         /**
14557          * Disable this KeyNav
14558          */
14559         disable: function(){
14560                 if(!this.disabled){
14561                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14562                 this.el.un("keydown", this.relay);
14563             }else{
14564                 this.el.un("keydown", this.prepareEvent);
14565                 this.el.un("keypress", this.relay);
14566             }
14567                     this.disabled = true;
14568                 }
14569         }
14570 };/*
14571  * Based on:
14572  * Ext JS Library 1.1.1
14573  * Copyright(c) 2006-2007, Ext JS, LLC.
14574  *
14575  * Originally Released Under LGPL - original licence link has changed is not relivant.
14576  *
14577  * Fork - LGPL
14578  * <script type="text/javascript">
14579  */
14580
14581  
14582 /**
14583  * @class Roo.KeyMap
14584  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14585  * The constructor accepts the same config object as defined by {@link #addBinding}.
14586  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14587  * combination it will call the function with this signature (if the match is a multi-key
14588  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14589  * A KeyMap can also handle a string representation of keys.<br />
14590  * Usage:
14591  <pre><code>
14592 // map one key by key code
14593 var map = new Roo.KeyMap("my-element", {
14594     key: 13, // or Roo.EventObject.ENTER
14595     fn: myHandler,
14596     scope: myObject
14597 });
14598
14599 // map multiple keys to one action by string
14600 var map = new Roo.KeyMap("my-element", {
14601     key: "a\r\n\t",
14602     fn: myHandler,
14603     scope: myObject
14604 });
14605
14606 // map multiple keys to multiple actions by strings and array of codes
14607 var map = new Roo.KeyMap("my-element", [
14608     {
14609         key: [10,13],
14610         fn: function(){ alert("Return was pressed"); }
14611     }, {
14612         key: "abc",
14613         fn: function(){ alert('a, b or c was pressed'); }
14614     }, {
14615         key: "\t",
14616         ctrl:true,
14617         shift:true,
14618         fn: function(){ alert('Control + shift + tab was pressed.'); }
14619     }
14620 ]);
14621 </code></pre>
14622  * <b>Note: A KeyMap starts enabled</b>
14623  * @constructor
14624  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14625  * @param {Object} config The config (see {@link #addBinding})
14626  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14627  */
14628 Roo.KeyMap = function(el, config, eventName){
14629     this.el  = Roo.get(el);
14630     this.eventName = eventName || "keydown";
14631     this.bindings = [];
14632     if(config){
14633         this.addBinding(config);
14634     }
14635     this.enable();
14636 };
14637
14638 Roo.KeyMap.prototype = {
14639     /**
14640      * True to stop the event from bubbling and prevent the default browser action if the
14641      * key was handled by the KeyMap (defaults to false)
14642      * @type Boolean
14643      */
14644     stopEvent : false,
14645
14646     /**
14647      * Add a new binding to this KeyMap. The following config object properties are supported:
14648      * <pre>
14649 Property    Type             Description
14650 ----------  ---------------  ----------------------------------------------------------------------
14651 key         String/Array     A single keycode or an array of keycodes to handle
14652 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14653 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14654 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14655 fn          Function         The function to call when KeyMap finds the expected key combination
14656 scope       Object           The scope of the callback function
14657 </pre>
14658      *
14659      * Usage:
14660      * <pre><code>
14661 // Create a KeyMap
14662 var map = new Roo.KeyMap(document, {
14663     key: Roo.EventObject.ENTER,
14664     fn: handleKey,
14665     scope: this
14666 });
14667
14668 //Add a new binding to the existing KeyMap later
14669 map.addBinding({
14670     key: 'abc',
14671     shift: true,
14672     fn: handleKey,
14673     scope: this
14674 });
14675 </code></pre>
14676      * @param {Object/Array} config A single KeyMap config or an array of configs
14677      */
14678         addBinding : function(config){
14679         if(config instanceof Array){
14680             for(var i = 0, len = config.length; i < len; i++){
14681                 this.addBinding(config[i]);
14682             }
14683             return;
14684         }
14685         var keyCode = config.key,
14686             shift = config.shift, 
14687             ctrl = config.ctrl, 
14688             alt = config.alt,
14689             fn = config.fn,
14690             scope = config.scope;
14691         if(typeof keyCode == "string"){
14692             var ks = [];
14693             var keyString = keyCode.toUpperCase();
14694             for(var j = 0, len = keyString.length; j < len; j++){
14695                 ks.push(keyString.charCodeAt(j));
14696             }
14697             keyCode = ks;
14698         }
14699         var keyArray = keyCode instanceof Array;
14700         var handler = function(e){
14701             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14702                 var k = e.getKey();
14703                 if(keyArray){
14704                     for(var i = 0, len = keyCode.length; i < len; i++){
14705                         if(keyCode[i] == k){
14706                           if(this.stopEvent){
14707                               e.stopEvent();
14708                           }
14709                           fn.call(scope || window, k, e);
14710                           return;
14711                         }
14712                     }
14713                 }else{
14714                     if(k == keyCode){
14715                         if(this.stopEvent){
14716                            e.stopEvent();
14717                         }
14718                         fn.call(scope || window, k, e);
14719                     }
14720                 }
14721             }
14722         };
14723         this.bindings.push(handler);  
14724         },
14725
14726     /**
14727      * Shorthand for adding a single key listener
14728      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14729      * following options:
14730      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14731      * @param {Function} fn The function to call
14732      * @param {Object} scope (optional) The scope of the function
14733      */
14734     on : function(key, fn, scope){
14735         var keyCode, shift, ctrl, alt;
14736         if(typeof key == "object" && !(key instanceof Array)){
14737             keyCode = key.key;
14738             shift = key.shift;
14739             ctrl = key.ctrl;
14740             alt = key.alt;
14741         }else{
14742             keyCode = key;
14743         }
14744         this.addBinding({
14745             key: keyCode,
14746             shift: shift,
14747             ctrl: ctrl,
14748             alt: alt,
14749             fn: fn,
14750             scope: scope
14751         })
14752     },
14753
14754     // private
14755     handleKeyDown : function(e){
14756             if(this.enabled){ //just in case
14757             var b = this.bindings;
14758             for(var i = 0, len = b.length; i < len; i++){
14759                 b[i].call(this, e);
14760             }
14761             }
14762         },
14763         
14764         /**
14765          * Returns true if this KeyMap is enabled
14766          * @return {Boolean} 
14767          */
14768         isEnabled : function(){
14769             return this.enabled;  
14770         },
14771         
14772         /**
14773          * Enables this KeyMap
14774          */
14775         enable: function(){
14776                 if(!this.enabled){
14777                     this.el.on(this.eventName, this.handleKeyDown, this);
14778                     this.enabled = true;
14779                 }
14780         },
14781
14782         /**
14783          * Disable this KeyMap
14784          */
14785         disable: function(){
14786                 if(this.enabled){
14787                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14788                     this.enabled = false;
14789                 }
14790         }
14791 };/*
14792  * Based on:
14793  * Ext JS Library 1.1.1
14794  * Copyright(c) 2006-2007, Ext JS, LLC.
14795  *
14796  * Originally Released Under LGPL - original licence link has changed is not relivant.
14797  *
14798  * Fork - LGPL
14799  * <script type="text/javascript">
14800  */
14801
14802  
14803 /**
14804  * @class Roo.util.TextMetrics
14805  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14806  * wide, in pixels, a given block of text will be.
14807  * @singleton
14808  */
14809 Roo.util.TextMetrics = function(){
14810     var shared;
14811     return {
14812         /**
14813          * Measures the size of the specified text
14814          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14815          * that can affect the size of the rendered text
14816          * @param {String} text The text to measure
14817          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14818          * in order to accurately measure the text height
14819          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14820          */
14821         measure : function(el, text, fixedWidth){
14822             if(!shared){
14823                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14824             }
14825             shared.bind(el);
14826             shared.setFixedWidth(fixedWidth || 'auto');
14827             return shared.getSize(text);
14828         },
14829
14830         /**
14831          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14832          * the overhead of multiple calls to initialize the style properties on each measurement.
14833          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14834          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14835          * in order to accurately measure the text height
14836          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14837          */
14838         createInstance : function(el, fixedWidth){
14839             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14840         }
14841     };
14842 }();
14843
14844  
14845
14846 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14847     var ml = new Roo.Element(document.createElement('div'));
14848     document.body.appendChild(ml.dom);
14849     ml.position('absolute');
14850     ml.setLeftTop(-1000, -1000);
14851     ml.hide();
14852
14853     if(fixedWidth){
14854         ml.setWidth(fixedWidth);
14855     }
14856      
14857     var instance = {
14858         /**
14859          * Returns the size of the specified text based on the internal element's style and width properties
14860          * @memberOf Roo.util.TextMetrics.Instance#
14861          * @param {String} text The text to measure
14862          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14863          */
14864         getSize : function(text){
14865             ml.update(text);
14866             var s = ml.getSize();
14867             ml.update('');
14868             return s;
14869         },
14870
14871         /**
14872          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14873          * that can affect the size of the rendered text
14874          * @memberOf Roo.util.TextMetrics.Instance#
14875          * @param {String/HTMLElement} el The element, dom node or id
14876          */
14877         bind : function(el){
14878             ml.setStyle(
14879                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14880             );
14881         },
14882
14883         /**
14884          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14885          * to set a fixed width in order to accurately measure the text height.
14886          * @memberOf Roo.util.TextMetrics.Instance#
14887          * @param {Number} width The width to set on the element
14888          */
14889         setFixedWidth : function(width){
14890             ml.setWidth(width);
14891         },
14892
14893         /**
14894          * Returns the measured width of the specified text
14895          * @memberOf Roo.util.TextMetrics.Instance#
14896          * @param {String} text The text to measure
14897          * @return {Number} width The width in pixels
14898          */
14899         getWidth : function(text){
14900             ml.dom.style.width = 'auto';
14901             return this.getSize(text).width;
14902         },
14903
14904         /**
14905          * Returns the measured height of the specified text.  For multiline text, be sure to call
14906          * {@link #setFixedWidth} if necessary.
14907          * @memberOf Roo.util.TextMetrics.Instance#
14908          * @param {String} text The text to measure
14909          * @return {Number} height The height in pixels
14910          */
14911         getHeight : function(text){
14912             return this.getSize(text).height;
14913         }
14914     };
14915
14916     instance.bind(bindTo);
14917
14918     return instance;
14919 };
14920
14921 // backwards compat
14922 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14923  * Based on:
14924  * Ext JS Library 1.1.1
14925  * Copyright(c) 2006-2007, Ext JS, LLC.
14926  *
14927  * Originally Released Under LGPL - original licence link has changed is not relivant.
14928  *
14929  * Fork - LGPL
14930  * <script type="text/javascript">
14931  */
14932
14933 /**
14934  * @class Roo.state.Provider
14935  * Abstract base class for state provider implementations. This class provides methods
14936  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14937  * Provider interface.
14938  */
14939 Roo.state.Provider = function(){
14940     /**
14941      * @event statechange
14942      * Fires when a state change occurs.
14943      * @param {Provider} this This state provider
14944      * @param {String} key The state key which was changed
14945      * @param {String} value The encoded value for the state
14946      */
14947     this.addEvents({
14948         "statechange": true
14949     });
14950     this.state = {};
14951     Roo.state.Provider.superclass.constructor.call(this);
14952 };
14953 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14954     /**
14955      * Returns the current value for a key
14956      * @param {String} name The key name
14957      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14958      * @return {Mixed} The state data
14959      */
14960     get : function(name, defaultValue){
14961         return typeof this.state[name] == "undefined" ?
14962             defaultValue : this.state[name];
14963     },
14964     
14965     /**
14966      * Clears a value from the state
14967      * @param {String} name The key name
14968      */
14969     clear : function(name){
14970         delete this.state[name];
14971         this.fireEvent("statechange", this, name, null);
14972     },
14973     
14974     /**
14975      * Sets the value for a key
14976      * @param {String} name The key name
14977      * @param {Mixed} value The value to set
14978      */
14979     set : function(name, value){
14980         this.state[name] = value;
14981         this.fireEvent("statechange", this, name, value);
14982     },
14983     
14984     /**
14985      * Decodes a string previously encoded with {@link #encodeValue}.
14986      * @param {String} value The value to decode
14987      * @return {Mixed} The decoded value
14988      */
14989     decodeValue : function(cookie){
14990         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14991         var matches = re.exec(unescape(cookie));
14992         if(!matches || !matches[1]) {
14993             return; // non state cookie
14994         }
14995         var type = matches[1];
14996         var v = matches[2];
14997         switch(type){
14998             case "n":
14999                 return parseFloat(v);
15000             case "d":
15001                 return new Date(Date.parse(v));
15002             case "b":
15003                 return (v == "1");
15004             case "a":
15005                 var all = [];
15006                 var values = v.split("^");
15007                 for(var i = 0, len = values.length; i < len; i++){
15008                     all.push(this.decodeValue(values[i]));
15009                 }
15010                 return all;
15011            case "o":
15012                 var all = {};
15013                 var values = v.split("^");
15014                 for(var i = 0, len = values.length; i < len; i++){
15015                     var kv = values[i].split("=");
15016                     all[kv[0]] = this.decodeValue(kv[1]);
15017                 }
15018                 return all;
15019            default:
15020                 return v;
15021         }
15022     },
15023     
15024     /**
15025      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15026      * @param {Mixed} value The value to encode
15027      * @return {String} The encoded value
15028      */
15029     encodeValue : function(v){
15030         var enc;
15031         if(typeof v == "number"){
15032             enc = "n:" + v;
15033         }else if(typeof v == "boolean"){
15034             enc = "b:" + (v ? "1" : "0");
15035         }else if(v instanceof Date){
15036             enc = "d:" + v.toGMTString();
15037         }else if(v instanceof Array){
15038             var flat = "";
15039             for(var i = 0, len = v.length; i < len; i++){
15040                 flat += this.encodeValue(v[i]);
15041                 if(i != len-1) {
15042                     flat += "^";
15043                 }
15044             }
15045             enc = "a:" + flat;
15046         }else if(typeof v == "object"){
15047             var flat = "";
15048             for(var key in v){
15049                 if(typeof v[key] != "function"){
15050                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15051                 }
15052             }
15053             enc = "o:" + flat.substring(0, flat.length-1);
15054         }else{
15055             enc = "s:" + v;
15056         }
15057         return escape(enc);        
15058     }
15059 });
15060
15061 /*
15062  * Based on:
15063  * Ext JS Library 1.1.1
15064  * Copyright(c) 2006-2007, Ext JS, LLC.
15065  *
15066  * Originally Released Under LGPL - original licence link has changed is not relivant.
15067  *
15068  * Fork - LGPL
15069  * <script type="text/javascript">
15070  */
15071 /**
15072  * @class Roo.state.Manager
15073  * This is the global state manager. By default all components that are "state aware" check this class
15074  * for state information if you don't pass them a custom state provider. In order for this class
15075  * to be useful, it must be initialized with a provider when your application initializes.
15076  <pre><code>
15077 // in your initialization function
15078 init : function(){
15079    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15080    ...
15081    // supposed you have a {@link Roo.BorderLayout}
15082    var layout = new Roo.BorderLayout(...);
15083    layout.restoreState();
15084    // or a {Roo.BasicDialog}
15085    var dialog = new Roo.BasicDialog(...);
15086    dialog.restoreState();
15087  </code></pre>
15088  * @singleton
15089  */
15090 Roo.state.Manager = function(){
15091     var provider = new Roo.state.Provider();
15092     
15093     return {
15094         /**
15095          * Configures the default state provider for your application
15096          * @param {Provider} stateProvider The state provider to set
15097          */
15098         setProvider : function(stateProvider){
15099             provider = stateProvider;
15100         },
15101         
15102         /**
15103          * Returns the current value for a key
15104          * @param {String} name The key name
15105          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15106          * @return {Mixed} The state data
15107          */
15108         get : function(key, defaultValue){
15109             return provider.get(key, defaultValue);
15110         },
15111         
15112         /**
15113          * Sets the value for a key
15114          * @param {String} name The key name
15115          * @param {Mixed} value The state data
15116          */
15117          set : function(key, value){
15118             provider.set(key, value);
15119         },
15120         
15121         /**
15122          * Clears a value from the state
15123          * @param {String} name The key name
15124          */
15125         clear : function(key){
15126             provider.clear(key);
15127         },
15128         
15129         /**
15130          * Gets the currently configured state provider
15131          * @return {Provider} The state provider
15132          */
15133         getProvider : function(){
15134             return provider;
15135         }
15136     };
15137 }();
15138 /*
15139  * Based on:
15140  * Ext JS Library 1.1.1
15141  * Copyright(c) 2006-2007, Ext JS, LLC.
15142  *
15143  * Originally Released Under LGPL - original licence link has changed is not relivant.
15144  *
15145  * Fork - LGPL
15146  * <script type="text/javascript">
15147  */
15148 /**
15149  * @class Roo.state.CookieProvider
15150  * @extends Roo.state.Provider
15151  * The default Provider implementation which saves state via cookies.
15152  * <br />Usage:
15153  <pre><code>
15154    var cp = new Roo.state.CookieProvider({
15155        path: "/cgi-bin/",
15156        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15157        domain: "roojs.com"
15158    })
15159    Roo.state.Manager.setProvider(cp);
15160  </code></pre>
15161  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15162  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15163  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15164  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15165  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15166  * domain the page is running on including the 'www' like 'www.roojs.com')
15167  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15168  * @constructor
15169  * Create a new CookieProvider
15170  * @param {Object} config The configuration object
15171  */
15172 Roo.state.CookieProvider = function(config){
15173     Roo.state.CookieProvider.superclass.constructor.call(this);
15174     this.path = "/";
15175     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15176     this.domain = null;
15177     this.secure = false;
15178     Roo.apply(this, config);
15179     this.state = this.readCookies();
15180 };
15181
15182 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15183     // private
15184     set : function(name, value){
15185         if(typeof value == "undefined" || value === null){
15186             this.clear(name);
15187             return;
15188         }
15189         this.setCookie(name, value);
15190         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15191     },
15192
15193     // private
15194     clear : function(name){
15195         this.clearCookie(name);
15196         Roo.state.CookieProvider.superclass.clear.call(this, name);
15197     },
15198
15199     // private
15200     readCookies : function(){
15201         var cookies = {};
15202         var c = document.cookie + ";";
15203         var re = /\s?(.*?)=(.*?);/g;
15204         var matches;
15205         while((matches = re.exec(c)) != null){
15206             var name = matches[1];
15207             var value = matches[2];
15208             if(name && name.substring(0,3) == "ys-"){
15209                 cookies[name.substr(3)] = this.decodeValue(value);
15210             }
15211         }
15212         return cookies;
15213     },
15214
15215     // private
15216     setCookie : function(name, value){
15217         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15218            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15219            ((this.path == null) ? "" : ("; path=" + this.path)) +
15220            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15221            ((this.secure == true) ? "; secure" : "");
15222     },
15223
15224     // private
15225     clearCookie : function(name){
15226         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15227            ((this.path == null) ? "" : ("; path=" + this.path)) +
15228            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15229            ((this.secure == true) ? "; secure" : "");
15230     }
15231 });/*
15232  * Based on:
15233  * Ext JS Library 1.1.1
15234  * Copyright(c) 2006-2007, Ext JS, LLC.
15235  *
15236  * Originally Released Under LGPL - original licence link has changed is not relivant.
15237  *
15238  * Fork - LGPL
15239  * <script type="text/javascript">
15240  */
15241  
15242
15243 /**
15244  * @class Roo.ComponentMgr
15245  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15246  * @singleton
15247  */
15248 Roo.ComponentMgr = function(){
15249     var all = new Roo.util.MixedCollection();
15250
15251     return {
15252         /**
15253          * Registers a component.
15254          * @param {Roo.Component} c The component
15255          */
15256         register : function(c){
15257             all.add(c);
15258         },
15259
15260         /**
15261          * Unregisters a component.
15262          * @param {Roo.Component} c The component
15263          */
15264         unregister : function(c){
15265             all.remove(c);
15266         },
15267
15268         /**
15269          * Returns a component by id
15270          * @param {String} id The component id
15271          */
15272         get : function(id){
15273             return all.get(id);
15274         },
15275
15276         /**
15277          * Registers a function that will be called when a specified component is added to ComponentMgr
15278          * @param {String} id The component id
15279          * @param {Funtction} fn The callback function
15280          * @param {Object} scope The scope of the callback
15281          */
15282         onAvailable : function(id, fn, scope){
15283             all.on("add", function(index, o){
15284                 if(o.id == id){
15285                     fn.call(scope || o, o);
15286                     all.un("add", fn, scope);
15287                 }
15288             });
15289         }
15290     };
15291 }();/*
15292  * Based on:
15293  * Ext JS Library 1.1.1
15294  * Copyright(c) 2006-2007, Ext JS, LLC.
15295  *
15296  * Originally Released Under LGPL - original licence link has changed is not relivant.
15297  *
15298  * Fork - LGPL
15299  * <script type="text/javascript">
15300  */
15301  
15302 /**
15303  * @class Roo.Component
15304  * @extends Roo.util.Observable
15305  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15306  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15307  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15308  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15309  * All visual components (widgets) that require rendering into a layout should subclass Component.
15310  * @constructor
15311  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15312  * 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
15313  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15314  */
15315 Roo.Component = function(config){
15316     config = config || {};
15317     if(config.tagName || config.dom || typeof config == "string"){ // element object
15318         config = {el: config, id: config.id || config};
15319     }
15320     this.initialConfig = config;
15321
15322     Roo.apply(this, config);
15323     this.addEvents({
15324         /**
15325          * @event disable
15326          * Fires after the component is disabled.
15327              * @param {Roo.Component} this
15328              */
15329         disable : true,
15330         /**
15331          * @event enable
15332          * Fires after the component is enabled.
15333              * @param {Roo.Component} this
15334              */
15335         enable : true,
15336         /**
15337          * @event beforeshow
15338          * Fires before the component is shown.  Return false to stop the show.
15339              * @param {Roo.Component} this
15340              */
15341         beforeshow : true,
15342         /**
15343          * @event show
15344          * Fires after the component is shown.
15345              * @param {Roo.Component} this
15346              */
15347         show : true,
15348         /**
15349          * @event beforehide
15350          * Fires before the component is hidden. Return false to stop the hide.
15351              * @param {Roo.Component} this
15352              */
15353         beforehide : true,
15354         /**
15355          * @event hide
15356          * Fires after the component is hidden.
15357              * @param {Roo.Component} this
15358              */
15359         hide : true,
15360         /**
15361          * @event beforerender
15362          * Fires before the component is rendered. Return false to stop the render.
15363              * @param {Roo.Component} this
15364              */
15365         beforerender : true,
15366         /**
15367          * @event render
15368          * Fires after the component is rendered.
15369              * @param {Roo.Component} this
15370              */
15371         render : true,
15372         /**
15373          * @event beforedestroy
15374          * Fires before the component is destroyed. Return false to stop the destroy.
15375              * @param {Roo.Component} this
15376              */
15377         beforedestroy : true,
15378         /**
15379          * @event destroy
15380          * Fires after the component is destroyed.
15381              * @param {Roo.Component} this
15382              */
15383         destroy : true
15384     });
15385     if(!this.id){
15386         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15387     }
15388     Roo.ComponentMgr.register(this);
15389     Roo.Component.superclass.constructor.call(this);
15390     this.initComponent();
15391     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15392         this.render(this.renderTo);
15393         delete this.renderTo;
15394     }
15395 };
15396
15397 /** @private */
15398 Roo.Component.AUTO_ID = 1000;
15399
15400 Roo.extend(Roo.Component, Roo.util.Observable, {
15401     /**
15402      * @scope Roo.Component.prototype
15403      * @type {Boolean}
15404      * true if this component is hidden. Read-only.
15405      */
15406     hidden : false,
15407     /**
15408      * @type {Boolean}
15409      * true if this component is disabled. Read-only.
15410      */
15411     disabled : false,
15412     /**
15413      * @type {Boolean}
15414      * true if this component has been rendered. Read-only.
15415      */
15416     rendered : false,
15417     
15418     /** @cfg {String} disableClass
15419      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15420      */
15421     disabledClass : "x-item-disabled",
15422         /** @cfg {Boolean} allowDomMove
15423          * Whether the component can move the Dom node when rendering (defaults to true).
15424          */
15425     allowDomMove : true,
15426     /** @cfg {String} hideMode (display|visibility)
15427      * How this component should hidden. Supported values are
15428      * "visibility" (css visibility), "offsets" (negative offset position) and
15429      * "display" (css display) - defaults to "display".
15430      */
15431     hideMode: 'display',
15432
15433     /** @private */
15434     ctype : "Roo.Component",
15435
15436     /**
15437      * @cfg {String} actionMode 
15438      * which property holds the element that used for  hide() / show() / disable() / enable()
15439      * default is 'el' 
15440      */
15441     actionMode : "el",
15442
15443     /** @private */
15444     getActionEl : function(){
15445         return this[this.actionMode];
15446     },
15447
15448     initComponent : Roo.emptyFn,
15449     /**
15450      * If this is a lazy rendering component, render it to its container element.
15451      * @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.
15452      */
15453     render : function(container, position){
15454         
15455         if(this.rendered){
15456             return this;
15457         }
15458         
15459         if(this.fireEvent("beforerender", this) === false){
15460             return false;
15461         }
15462         
15463         if(!container && this.el){
15464             this.el = Roo.get(this.el);
15465             container = this.el.dom.parentNode;
15466             this.allowDomMove = false;
15467         }
15468         this.container = Roo.get(container);
15469         this.rendered = true;
15470         if(position !== undefined){
15471             if(typeof position == 'number'){
15472                 position = this.container.dom.childNodes[position];
15473             }else{
15474                 position = Roo.getDom(position);
15475             }
15476         }
15477         this.onRender(this.container, position || null);
15478         if(this.cls){
15479             this.el.addClass(this.cls);
15480             delete this.cls;
15481         }
15482         if(this.style){
15483             this.el.applyStyles(this.style);
15484             delete this.style;
15485         }
15486         this.fireEvent("render", this);
15487         this.afterRender(this.container);
15488         if(this.hidden){
15489             this.hide();
15490         }
15491         if(this.disabled){
15492             this.disable();
15493         }
15494
15495         return this;
15496         
15497     },
15498
15499     /** @private */
15500     // default function is not really useful
15501     onRender : function(ct, position){
15502         if(this.el){
15503             this.el = Roo.get(this.el);
15504             if(this.allowDomMove !== false){
15505                 ct.dom.insertBefore(this.el.dom, position);
15506             }
15507         }
15508     },
15509
15510     /** @private */
15511     getAutoCreate : function(){
15512         var cfg = typeof this.autoCreate == "object" ?
15513                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15514         if(this.id && !cfg.id){
15515             cfg.id = this.id;
15516         }
15517         return cfg;
15518     },
15519
15520     /** @private */
15521     afterRender : Roo.emptyFn,
15522
15523     /**
15524      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15525      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15526      */
15527     destroy : function(){
15528         if(this.fireEvent("beforedestroy", this) !== false){
15529             this.purgeListeners();
15530             this.beforeDestroy();
15531             if(this.rendered){
15532                 this.el.removeAllListeners();
15533                 this.el.remove();
15534                 if(this.actionMode == "container"){
15535                     this.container.remove();
15536                 }
15537             }
15538             this.onDestroy();
15539             Roo.ComponentMgr.unregister(this);
15540             this.fireEvent("destroy", this);
15541         }
15542     },
15543
15544         /** @private */
15545     beforeDestroy : function(){
15546
15547     },
15548
15549         /** @private */
15550         onDestroy : function(){
15551
15552     },
15553
15554     /**
15555      * Returns the underlying {@link Roo.Element}.
15556      * @return {Roo.Element} The element
15557      */
15558     getEl : function(){
15559         return this.el;
15560     },
15561
15562     /**
15563      * Returns the id of this component.
15564      * @return {String}
15565      */
15566     getId : function(){
15567         return this.id;
15568     },
15569
15570     /**
15571      * Try to focus this component.
15572      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15573      * @return {Roo.Component} this
15574      */
15575     focus : function(selectText){
15576         if(this.rendered){
15577             this.el.focus();
15578             if(selectText === true){
15579                 this.el.dom.select();
15580             }
15581         }
15582         return this;
15583     },
15584
15585     /** @private */
15586     blur : function(){
15587         if(this.rendered){
15588             this.el.blur();
15589         }
15590         return this;
15591     },
15592
15593     /**
15594      * Disable this component.
15595      * @return {Roo.Component} this
15596      */
15597     disable : function(){
15598         if(this.rendered){
15599             this.onDisable();
15600         }
15601         this.disabled = true;
15602         this.fireEvent("disable", this);
15603         return this;
15604     },
15605
15606         // private
15607     onDisable : function(){
15608         this.getActionEl().addClass(this.disabledClass);
15609         this.el.dom.disabled = true;
15610     },
15611
15612     /**
15613      * Enable this component.
15614      * @return {Roo.Component} this
15615      */
15616     enable : function(){
15617         if(this.rendered){
15618             this.onEnable();
15619         }
15620         this.disabled = false;
15621         this.fireEvent("enable", this);
15622         return this;
15623     },
15624
15625         // private
15626     onEnable : function(){
15627         this.getActionEl().removeClass(this.disabledClass);
15628         this.el.dom.disabled = false;
15629     },
15630
15631     /**
15632      * Convenience function for setting disabled/enabled by boolean.
15633      * @param {Boolean} disabled
15634      */
15635     setDisabled : function(disabled){
15636         this[disabled ? "disable" : "enable"]();
15637     },
15638
15639     /**
15640      * Show this component.
15641      * @return {Roo.Component} this
15642      */
15643     show: function(){
15644         if(this.fireEvent("beforeshow", this) !== false){
15645             this.hidden = false;
15646             if(this.rendered){
15647                 this.onShow();
15648             }
15649             this.fireEvent("show", this);
15650         }
15651         return this;
15652     },
15653
15654     // private
15655     onShow : function(){
15656         var ae = this.getActionEl();
15657         if(this.hideMode == 'visibility'){
15658             ae.dom.style.visibility = "visible";
15659         }else if(this.hideMode == 'offsets'){
15660             ae.removeClass('x-hidden');
15661         }else{
15662             ae.dom.style.display = "";
15663         }
15664     },
15665
15666     /**
15667      * Hide this component.
15668      * @return {Roo.Component} this
15669      */
15670     hide: function(){
15671         if(this.fireEvent("beforehide", this) !== false){
15672             this.hidden = true;
15673             if(this.rendered){
15674                 this.onHide();
15675             }
15676             this.fireEvent("hide", this);
15677         }
15678         return this;
15679     },
15680
15681     // private
15682     onHide : function(){
15683         var ae = this.getActionEl();
15684         if(this.hideMode == 'visibility'){
15685             ae.dom.style.visibility = "hidden";
15686         }else if(this.hideMode == 'offsets'){
15687             ae.addClass('x-hidden');
15688         }else{
15689             ae.dom.style.display = "none";
15690         }
15691     },
15692
15693     /**
15694      * Convenience function to hide or show this component by boolean.
15695      * @param {Boolean} visible True to show, false to hide
15696      * @return {Roo.Component} this
15697      */
15698     setVisible: function(visible){
15699         if(visible) {
15700             this.show();
15701         }else{
15702             this.hide();
15703         }
15704         return this;
15705     },
15706
15707     /**
15708      * Returns true if this component is visible.
15709      */
15710     isVisible : function(){
15711         return this.getActionEl().isVisible();
15712     },
15713
15714     cloneConfig : function(overrides){
15715         overrides = overrides || {};
15716         var id = overrides.id || Roo.id();
15717         var cfg = Roo.applyIf(overrides, this.initialConfig);
15718         cfg.id = id; // prevent dup id
15719         return new this.constructor(cfg);
15720     }
15721 });/*
15722  * Based on:
15723  * Ext JS Library 1.1.1
15724  * Copyright(c) 2006-2007, Ext JS, LLC.
15725  *
15726  * Originally Released Under LGPL - original licence link has changed is not relivant.
15727  *
15728  * Fork - LGPL
15729  * <script type="text/javascript">
15730  */
15731
15732 /**
15733  * @class Roo.BoxComponent
15734  * @extends Roo.Component
15735  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15736  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15737  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15738  * layout containers.
15739  * @constructor
15740  * @param {Roo.Element/String/Object} config The configuration options.
15741  */
15742 Roo.BoxComponent = function(config){
15743     Roo.Component.call(this, config);
15744     this.addEvents({
15745         /**
15746          * @event resize
15747          * Fires after the component is resized.
15748              * @param {Roo.Component} this
15749              * @param {Number} adjWidth The box-adjusted width that was set
15750              * @param {Number} adjHeight The box-adjusted height that was set
15751              * @param {Number} rawWidth The width that was originally specified
15752              * @param {Number} rawHeight The height that was originally specified
15753              */
15754         resize : true,
15755         /**
15756          * @event move
15757          * Fires after the component is moved.
15758              * @param {Roo.Component} this
15759              * @param {Number} x The new x position
15760              * @param {Number} y The new y position
15761              */
15762         move : true
15763     });
15764 };
15765
15766 Roo.extend(Roo.BoxComponent, Roo.Component, {
15767     // private, set in afterRender to signify that the component has been rendered
15768     boxReady : false,
15769     // private, used to defer height settings to subclasses
15770     deferHeight: false,
15771     /** @cfg {Number} width
15772      * width (optional) size of component
15773      */
15774      /** @cfg {Number} height
15775      * height (optional) size of component
15776      */
15777      
15778     /**
15779      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15780      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15781      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15782      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15783      * @return {Roo.BoxComponent} this
15784      */
15785     setSize : function(w, h){
15786         // support for standard size objects
15787         if(typeof w == 'object'){
15788             h = w.height;
15789             w = w.width;
15790         }
15791         // not rendered
15792         if(!this.boxReady){
15793             this.width = w;
15794             this.height = h;
15795             return this;
15796         }
15797
15798         // prevent recalcs when not needed
15799         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15800             return this;
15801         }
15802         this.lastSize = {width: w, height: h};
15803
15804         var adj = this.adjustSize(w, h);
15805         var aw = adj.width, ah = adj.height;
15806         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15807             var rz = this.getResizeEl();
15808             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15809                 rz.setSize(aw, ah);
15810             }else if(!this.deferHeight && ah !== undefined){
15811                 rz.setHeight(ah);
15812             }else if(aw !== undefined){
15813                 rz.setWidth(aw);
15814             }
15815             this.onResize(aw, ah, w, h);
15816             this.fireEvent('resize', this, aw, ah, w, h);
15817         }
15818         return this;
15819     },
15820
15821     /**
15822      * Gets the current size of the component's underlying element.
15823      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15824      */
15825     getSize : function(){
15826         return this.el.getSize();
15827     },
15828
15829     /**
15830      * Gets the current XY position of the component's underlying element.
15831      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15832      * @return {Array} The XY position of the element (e.g., [100, 200])
15833      */
15834     getPosition : function(local){
15835         if(local === true){
15836             return [this.el.getLeft(true), this.el.getTop(true)];
15837         }
15838         return this.xy || this.el.getXY();
15839     },
15840
15841     /**
15842      * Gets the current box measurements of the component's underlying element.
15843      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15844      * @returns {Object} box An object in the format {x, y, width, height}
15845      */
15846     getBox : function(local){
15847         var s = this.el.getSize();
15848         if(local){
15849             s.x = this.el.getLeft(true);
15850             s.y = this.el.getTop(true);
15851         }else{
15852             var xy = this.xy || this.el.getXY();
15853             s.x = xy[0];
15854             s.y = xy[1];
15855         }
15856         return s;
15857     },
15858
15859     /**
15860      * Sets the current box measurements of the component's underlying element.
15861      * @param {Object} box An object in the format {x, y, width, height}
15862      * @returns {Roo.BoxComponent} this
15863      */
15864     updateBox : function(box){
15865         this.setSize(box.width, box.height);
15866         this.setPagePosition(box.x, box.y);
15867         return this;
15868     },
15869
15870     // protected
15871     getResizeEl : function(){
15872         return this.resizeEl || this.el;
15873     },
15874
15875     // protected
15876     getPositionEl : function(){
15877         return this.positionEl || this.el;
15878     },
15879
15880     /**
15881      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15882      * This method fires the move event.
15883      * @param {Number} left The new left
15884      * @param {Number} top The new top
15885      * @returns {Roo.BoxComponent} this
15886      */
15887     setPosition : function(x, y){
15888         this.x = x;
15889         this.y = y;
15890         if(!this.boxReady){
15891             return this;
15892         }
15893         var adj = this.adjustPosition(x, y);
15894         var ax = adj.x, ay = adj.y;
15895
15896         var el = this.getPositionEl();
15897         if(ax !== undefined || ay !== undefined){
15898             if(ax !== undefined && ay !== undefined){
15899                 el.setLeftTop(ax, ay);
15900             }else if(ax !== undefined){
15901                 el.setLeft(ax);
15902             }else if(ay !== undefined){
15903                 el.setTop(ay);
15904             }
15905             this.onPosition(ax, ay);
15906             this.fireEvent('move', this, ax, ay);
15907         }
15908         return this;
15909     },
15910
15911     /**
15912      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15913      * This method fires the move event.
15914      * @param {Number} x The new x position
15915      * @param {Number} y The new y position
15916      * @returns {Roo.BoxComponent} this
15917      */
15918     setPagePosition : function(x, y){
15919         this.pageX = x;
15920         this.pageY = y;
15921         if(!this.boxReady){
15922             return;
15923         }
15924         if(x === undefined || y === undefined){ // cannot translate undefined points
15925             return;
15926         }
15927         var p = this.el.translatePoints(x, y);
15928         this.setPosition(p.left, p.top);
15929         return this;
15930     },
15931
15932     // private
15933     onRender : function(ct, position){
15934         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15935         if(this.resizeEl){
15936             this.resizeEl = Roo.get(this.resizeEl);
15937         }
15938         if(this.positionEl){
15939             this.positionEl = Roo.get(this.positionEl);
15940         }
15941     },
15942
15943     // private
15944     afterRender : function(){
15945         Roo.BoxComponent.superclass.afterRender.call(this);
15946         this.boxReady = true;
15947         this.setSize(this.width, this.height);
15948         if(this.x || this.y){
15949             this.setPosition(this.x, this.y);
15950         }
15951         if(this.pageX || this.pageY){
15952             this.setPagePosition(this.pageX, this.pageY);
15953         }
15954     },
15955
15956     /**
15957      * Force the component's size to recalculate based on the underlying element's current height and width.
15958      * @returns {Roo.BoxComponent} this
15959      */
15960     syncSize : function(){
15961         delete this.lastSize;
15962         this.setSize(this.el.getWidth(), this.el.getHeight());
15963         return this;
15964     },
15965
15966     /**
15967      * Called after the component is resized, this method is empty by default but can be implemented by any
15968      * subclass that needs to perform custom logic after a resize occurs.
15969      * @param {Number} adjWidth The box-adjusted width that was set
15970      * @param {Number} adjHeight The box-adjusted height that was set
15971      * @param {Number} rawWidth The width that was originally specified
15972      * @param {Number} rawHeight The height that was originally specified
15973      */
15974     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15975
15976     },
15977
15978     /**
15979      * Called after the component is moved, this method is empty by default but can be implemented by any
15980      * subclass that needs to perform custom logic after a move occurs.
15981      * @param {Number} x The new x position
15982      * @param {Number} y The new y position
15983      */
15984     onPosition : function(x, y){
15985
15986     },
15987
15988     // private
15989     adjustSize : function(w, h){
15990         if(this.autoWidth){
15991             w = 'auto';
15992         }
15993         if(this.autoHeight){
15994             h = 'auto';
15995         }
15996         return {width : w, height: h};
15997     },
15998
15999     // private
16000     adjustPosition : function(x, y){
16001         return {x : x, y: y};
16002     }
16003 });/*
16004  * Original code for Roojs - LGPL
16005  * <script type="text/javascript">
16006  */
16007  
16008 /**
16009  * @class Roo.XComponent
16010  * A delayed Element creator...
16011  * Or a way to group chunks of interface together.
16012  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16013  *  used in conjunction with XComponent.build() it will create an instance of each element,
16014  *  then call addxtype() to build the User interface.
16015  * 
16016  * Mypart.xyx = new Roo.XComponent({
16017
16018     parent : 'Mypart.xyz', // empty == document.element.!!
16019     order : '001',
16020     name : 'xxxx'
16021     region : 'xxxx'
16022     disabled : function() {} 
16023      
16024     tree : function() { // return an tree of xtype declared components
16025         var MODULE = this;
16026         return 
16027         {
16028             xtype : 'NestedLayoutPanel',
16029             // technicall
16030         }
16031      ]
16032  *})
16033  *
16034  *
16035  * It can be used to build a big heiracy, with parent etc.
16036  * or you can just use this to render a single compoent to a dom element
16037  * MYPART.render(Roo.Element | String(id) | dom_element )
16038  *
16039  *
16040  * Usage patterns.
16041  *
16042  * Classic Roo
16043  *
16044  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16045  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16046  *
16047  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16048  *
16049  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16050  * - if mulitple topModules exist, the last one is defined as the top module.
16051  *
16052  * Embeded Roo
16053  * 
16054  * When the top level or multiple modules are to embedded into a existing HTML page,
16055  * the parent element can container '#id' of the element where the module will be drawn.
16056  *
16057  * Bootstrap Roo
16058  *
16059  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16060  * it relies more on a include mechanism, where sub modules are included into an outer page.
16061  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16062  * 
16063  * Bootstrap Roo Included elements
16064  *
16065  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16066  * hence confusing the component builder as it thinks there are multiple top level elements. 
16067  *
16068  * String Over-ride & Translations
16069  *
16070  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16071  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16072  * are needed. @see Roo.XComponent.overlayString  
16073  * 
16074  * 
16075  * 
16076  * @extends Roo.util.Observable
16077  * @constructor
16078  * @param cfg {Object} configuration of component
16079  * 
16080  */
16081 Roo.XComponent = function(cfg) {
16082     Roo.apply(this, cfg);
16083     this.addEvents({ 
16084         /**
16085              * @event built
16086              * Fires when this the componnt is built
16087              * @param {Roo.XComponent} c the component
16088              */
16089         'built' : true
16090         
16091     });
16092     this.region = this.region || 'center'; // default..
16093     Roo.XComponent.register(this);
16094     this.modules = false;
16095     this.el = false; // where the layout goes..
16096     
16097     
16098 }
16099 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16100     /**
16101      * @property el
16102      * The created element (with Roo.factory())
16103      * @type {Roo.Layout}
16104      */
16105     el  : false,
16106     
16107     /**
16108      * @property el
16109      * for BC  - use el in new code
16110      * @type {Roo.Layout}
16111      */
16112     panel : false,
16113     
16114     /**
16115      * @property layout
16116      * for BC  - use el in new code
16117      * @type {Roo.Layout}
16118      */
16119     layout : false,
16120     
16121      /**
16122      * @cfg {Function|boolean} disabled
16123      * If this module is disabled by some rule, return true from the funtion
16124      */
16125     disabled : false,
16126     
16127     /**
16128      * @cfg {String} parent 
16129      * Name of parent element which it get xtype added to..
16130      */
16131     parent: false,
16132     
16133     /**
16134      * @cfg {String} order
16135      * Used to set the order in which elements are created (usefull for multiple tabs)
16136      */
16137     
16138     order : false,
16139     /**
16140      * @cfg {String} name
16141      * String to display while loading.
16142      */
16143     name : false,
16144     /**
16145      * @cfg {String} region
16146      * Region to render component to (defaults to center)
16147      */
16148     region : 'center',
16149     
16150     /**
16151      * @cfg {Array} items
16152      * A single item array - the first element is the root of the tree..
16153      * It's done this way to stay compatible with the Xtype system...
16154      */
16155     items : false,
16156     
16157     /**
16158      * @property _tree
16159      * The method that retuns the tree of parts that make up this compoennt 
16160      * @type {function}
16161      */
16162     _tree  : false,
16163     
16164      /**
16165      * render
16166      * render element to dom or tree
16167      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16168      */
16169     
16170     render : function(el)
16171     {
16172         
16173         el = el || false;
16174         var hp = this.parent ? 1 : 0;
16175         Roo.debug &&  Roo.log(this);
16176         
16177         var tree = this._tree ? this._tree() : this.tree();
16178
16179         
16180         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16181             // if parent is a '#.....' string, then let's use that..
16182             var ename = this.parent.substr(1);
16183             this.parent = false;
16184             Roo.debug && Roo.log(ename);
16185             switch (ename) {
16186                 case 'bootstrap-body':
16187                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16188                         // this is the BorderLayout standard?
16189                        this.parent = { el : true };
16190                        break;
16191                     }
16192                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16193                         // need to insert stuff...
16194                         this.parent =  {
16195                              el : new Roo.bootstrap.layout.Border({
16196                                  el : document.body, 
16197                      
16198                                  center: {
16199                                     titlebar: false,
16200                                     autoScroll:false,
16201                                     closeOnTab: true,
16202                                     tabPosition: 'top',
16203                                       //resizeTabs: true,
16204                                     alwaysShowTabs: true,
16205                                     hideTabs: false
16206                                      //minTabWidth: 140
16207                                  }
16208                              })
16209                         
16210                          };
16211                          break;
16212                     }
16213                          
16214                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16215                         this.parent = { el :  new  Roo.bootstrap.Body() };
16216                         Roo.debug && Roo.log("setting el to doc body");
16217                          
16218                     } else {
16219                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16220                     }
16221                     break;
16222                 case 'bootstrap':
16223                     this.parent = { el : true};
16224                     // fall through
16225                 default:
16226                     el = Roo.get(ename);
16227                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16228                         this.parent = { el : true};
16229                     }
16230                     
16231                     break;
16232             }
16233                 
16234             
16235             if (!el && !this.parent) {
16236                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16237                 return;
16238             }
16239         }
16240         
16241         Roo.debug && Roo.log("EL:");
16242         Roo.debug && Roo.log(el);
16243         Roo.debug && Roo.log("this.parent.el:");
16244         Roo.debug && Roo.log(this.parent.el);
16245         
16246
16247         // altertive root elements ??? - we need a better way to indicate these.
16248         var is_alt = Roo.XComponent.is_alt ||
16249                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16250                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16251                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16252         
16253         
16254         
16255         if (!this.parent && is_alt) {
16256             //el = Roo.get(document.body);
16257             this.parent = { el : true };
16258         }
16259             
16260             
16261         
16262         if (!this.parent) {
16263             
16264             Roo.debug && Roo.log("no parent - creating one");
16265             
16266             el = el ? Roo.get(el) : false;      
16267             
16268             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16269                 
16270                 this.parent =  {
16271                     el : new Roo.bootstrap.layout.Border({
16272                         el: el || document.body,
16273                     
16274                         center: {
16275                             titlebar: false,
16276                             autoScroll:false,
16277                             closeOnTab: true,
16278                             tabPosition: 'top',
16279                              //resizeTabs: true,
16280                             alwaysShowTabs: false,
16281                             hideTabs: true,
16282                             minTabWidth: 140,
16283                             overflow: 'visible'
16284                          }
16285                      })
16286                 };
16287             } else {
16288             
16289                 // it's a top level one..
16290                 this.parent =  {
16291                     el : new Roo.BorderLayout(el || document.body, {
16292                         center: {
16293                             titlebar: false,
16294                             autoScroll:false,
16295                             closeOnTab: true,
16296                             tabPosition: 'top',
16297                              //resizeTabs: true,
16298                             alwaysShowTabs: el && hp? false :  true,
16299                             hideTabs: el || !hp ? true :  false,
16300                             minTabWidth: 140
16301                          }
16302                     })
16303                 };
16304             }
16305         }
16306         
16307         if (!this.parent.el) {
16308                 // probably an old style ctor, which has been disabled.
16309                 return;
16310
16311         }
16312                 // The 'tree' method is  '_tree now' 
16313             
16314         tree.region = tree.region || this.region;
16315         var is_body = false;
16316         if (this.parent.el === true) {
16317             // bootstrap... - body..
16318             if (el) {
16319                 tree.el = el;
16320             }
16321             this.parent.el = Roo.factory(tree);
16322             is_body = true;
16323         }
16324         
16325         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16326         this.fireEvent('built', this);
16327         
16328         this.panel = this.el;
16329         this.layout = this.panel.layout;
16330         this.parentLayout = this.parent.layout  || false;  
16331          
16332     }
16333     
16334 });
16335
16336 Roo.apply(Roo.XComponent, {
16337     /**
16338      * @property  hideProgress
16339      * true to disable the building progress bar.. usefull on single page renders.
16340      * @type Boolean
16341      */
16342     hideProgress : false,
16343     /**
16344      * @property  buildCompleted
16345      * True when the builder has completed building the interface.
16346      * @type Boolean
16347      */
16348     buildCompleted : false,
16349      
16350     /**
16351      * @property  topModule
16352      * the upper most module - uses document.element as it's constructor.
16353      * @type Object
16354      */
16355      
16356     topModule  : false,
16357       
16358     /**
16359      * @property  modules
16360      * array of modules to be created by registration system.
16361      * @type {Array} of Roo.XComponent
16362      */
16363     
16364     modules : [],
16365     /**
16366      * @property  elmodules
16367      * array of modules to be created by which use #ID 
16368      * @type {Array} of Roo.XComponent
16369      */
16370      
16371     elmodules : [],
16372
16373      /**
16374      * @property  is_alt
16375      * Is an alternative Root - normally used by bootstrap or other systems,
16376      *    where the top element in the tree can wrap 'body' 
16377      * @type {boolean}  (default false)
16378      */
16379      
16380     is_alt : false,
16381     /**
16382      * @property  build_from_html
16383      * Build elements from html - used by bootstrap HTML stuff 
16384      *    - this is cleared after build is completed
16385      * @type {boolean}    (default false)
16386      */
16387      
16388     build_from_html : false,
16389     /**
16390      * Register components to be built later.
16391      *
16392      * This solves the following issues
16393      * - Building is not done on page load, but after an authentication process has occured.
16394      * - Interface elements are registered on page load
16395      * - Parent Interface elements may not be loaded before child, so this handles that..
16396      * 
16397      *
16398      * example:
16399      * 
16400      * MyApp.register({
16401           order : '000001',
16402           module : 'Pman.Tab.projectMgr',
16403           region : 'center',
16404           parent : 'Pman.layout',
16405           disabled : false,  // or use a function..
16406         })
16407      
16408      * * @param {Object} details about module
16409      */
16410     register : function(obj) {
16411                 
16412         Roo.XComponent.event.fireEvent('register', obj);
16413         switch(typeof(obj.disabled) ) {
16414                 
16415             case 'undefined':
16416                 break;
16417             
16418             case 'function':
16419                 if ( obj.disabled() ) {
16420                         return;
16421                 }
16422                 break;
16423             
16424             default:
16425                 if (obj.disabled || obj.region == '#disabled') {
16426                         return;
16427                 }
16428                 break;
16429         }
16430                 
16431         this.modules.push(obj);
16432          
16433     },
16434     /**
16435      * convert a string to an object..
16436      * eg. 'AAA.BBB' -> finds AAA.BBB
16437
16438      */
16439     
16440     toObject : function(str)
16441     {
16442         if (!str || typeof(str) == 'object') {
16443             return str;
16444         }
16445         if (str.substring(0,1) == '#') {
16446             return str;
16447         }
16448
16449         var ar = str.split('.');
16450         var rt, o;
16451         rt = ar.shift();
16452             /** eval:var:o */
16453         try {
16454             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16455         } catch (e) {
16456             throw "Module not found : " + str;
16457         }
16458         
16459         if (o === false) {
16460             throw "Module not found : " + str;
16461         }
16462         Roo.each(ar, function(e) {
16463             if (typeof(o[e]) == 'undefined') {
16464                 throw "Module not found : " + str;
16465             }
16466             o = o[e];
16467         });
16468         
16469         return o;
16470         
16471     },
16472     
16473     
16474     /**
16475      * move modules into their correct place in the tree..
16476      * 
16477      */
16478     preBuild : function ()
16479     {
16480         var _t = this;
16481         Roo.each(this.modules , function (obj)
16482         {
16483             Roo.XComponent.event.fireEvent('beforebuild', obj);
16484             
16485             var opar = obj.parent;
16486             try { 
16487                 obj.parent = this.toObject(opar);
16488             } catch(e) {
16489                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16490                 return;
16491             }
16492             
16493             if (!obj.parent) {
16494                 Roo.debug && Roo.log("GOT top level module");
16495                 Roo.debug && Roo.log(obj);
16496                 obj.modules = new Roo.util.MixedCollection(false, 
16497                     function(o) { return o.order + '' }
16498                 );
16499                 this.topModule = obj;
16500                 return;
16501             }
16502                         // parent is a string (usually a dom element name..)
16503             if (typeof(obj.parent) == 'string') {
16504                 this.elmodules.push(obj);
16505                 return;
16506             }
16507             if (obj.parent.constructor != Roo.XComponent) {
16508                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16509             }
16510             if (!obj.parent.modules) {
16511                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16512                     function(o) { return o.order + '' }
16513                 );
16514             }
16515             if (obj.parent.disabled) {
16516                 obj.disabled = true;
16517             }
16518             obj.parent.modules.add(obj);
16519         }, this);
16520     },
16521     
16522      /**
16523      * make a list of modules to build.
16524      * @return {Array} list of modules. 
16525      */ 
16526     
16527     buildOrder : function()
16528     {
16529         var _this = this;
16530         var cmp = function(a,b) {   
16531             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16532         };
16533         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16534             throw "No top level modules to build";
16535         }
16536         
16537         // make a flat list in order of modules to build.
16538         var mods = this.topModule ? [ this.topModule ] : [];
16539                 
16540         
16541         // elmodules (is a list of DOM based modules )
16542         Roo.each(this.elmodules, function(e) {
16543             mods.push(e);
16544             if (!this.topModule &&
16545                 typeof(e.parent) == 'string' &&
16546                 e.parent.substring(0,1) == '#' &&
16547                 Roo.get(e.parent.substr(1))
16548                ) {
16549                 
16550                 _this.topModule = e;
16551             }
16552             
16553         });
16554
16555         
16556         // add modules to their parents..
16557         var addMod = function(m) {
16558             Roo.debug && Roo.log("build Order: add: " + m.name);
16559                 
16560             mods.push(m);
16561             if (m.modules && !m.disabled) {
16562                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16563                 m.modules.keySort('ASC',  cmp );
16564                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16565     
16566                 m.modules.each(addMod);
16567             } else {
16568                 Roo.debug && Roo.log("build Order: no child modules");
16569             }
16570             // not sure if this is used any more..
16571             if (m.finalize) {
16572                 m.finalize.name = m.name + " (clean up) ";
16573                 mods.push(m.finalize);
16574             }
16575             
16576         }
16577         if (this.topModule && this.topModule.modules) { 
16578             this.topModule.modules.keySort('ASC',  cmp );
16579             this.topModule.modules.each(addMod);
16580         } 
16581         return mods;
16582     },
16583     
16584      /**
16585      * Build the registered modules.
16586      * @param {Object} parent element.
16587      * @param {Function} optional method to call after module has been added.
16588      * 
16589      */ 
16590    
16591     build : function(opts) 
16592     {
16593         
16594         if (typeof(opts) != 'undefined') {
16595             Roo.apply(this,opts);
16596         }
16597         
16598         this.preBuild();
16599         var mods = this.buildOrder();
16600       
16601         //this.allmods = mods;
16602         //Roo.debug && Roo.log(mods);
16603         //return;
16604         if (!mods.length) { // should not happen
16605             throw "NO modules!!!";
16606         }
16607         
16608         
16609         var msg = "Building Interface...";
16610         // flash it up as modal - so we store the mask!?
16611         if (!this.hideProgress && Roo.MessageBox) {
16612             Roo.MessageBox.show({ title: 'loading' });
16613             Roo.MessageBox.show({
16614                title: "Please wait...",
16615                msg: msg,
16616                width:450,
16617                progress:true,
16618                buttons : false,
16619                closable:false,
16620                modal: false
16621               
16622             });
16623         }
16624         var total = mods.length;
16625         
16626         var _this = this;
16627         var progressRun = function() {
16628             if (!mods.length) {
16629                 Roo.debug && Roo.log('hide?');
16630                 if (!this.hideProgress && Roo.MessageBox) {
16631                     Roo.MessageBox.hide();
16632                 }
16633                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16634                 
16635                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16636                 
16637                 // THE END...
16638                 return false;   
16639             }
16640             
16641             var m = mods.shift();
16642             
16643             
16644             Roo.debug && Roo.log(m);
16645             // not sure if this is supported any more.. - modules that are are just function
16646             if (typeof(m) == 'function') { 
16647                 m.call(this);
16648                 return progressRun.defer(10, _this);
16649             } 
16650             
16651             
16652             msg = "Building Interface " + (total  - mods.length) + 
16653                     " of " + total + 
16654                     (m.name ? (' - ' + m.name) : '');
16655                         Roo.debug && Roo.log(msg);
16656             if (!_this.hideProgress &&  Roo.MessageBox) { 
16657                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16658             }
16659             
16660          
16661             // is the module disabled?
16662             var disabled = (typeof(m.disabled) == 'function') ?
16663                 m.disabled.call(m.module.disabled) : m.disabled;    
16664             
16665             
16666             if (disabled) {
16667                 return progressRun(); // we do not update the display!
16668             }
16669             
16670             // now build 
16671             
16672                         
16673                         
16674             m.render();
16675             // it's 10 on top level, and 1 on others??? why...
16676             return progressRun.defer(10, _this);
16677              
16678         }
16679         progressRun.defer(1, _this);
16680      
16681         
16682         
16683     },
16684     /**
16685      * Overlay a set of modified strings onto a component
16686      * This is dependant on our builder exporting the strings and 'named strings' elements.
16687      * 
16688      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16689      * @param {Object} associative array of 'named' string and it's new value.
16690      * 
16691      */
16692         overlayStrings : function( component, strings )
16693     {
16694         if (typeof(component['_named_strings']) == 'undefined') {
16695             throw "ERROR: component does not have _named_strings";
16696         }
16697         for ( var k in strings ) {
16698             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16699             if (md !== false) {
16700                 component['_strings'][md] = strings[k];
16701             } else {
16702                 Roo.log('could not find named string: ' + k + ' in');
16703                 Roo.log(component);
16704             }
16705             
16706         }
16707         
16708     },
16709     
16710         
16711         /**
16712          * Event Object.
16713          *
16714          *
16715          */
16716         event: false, 
16717     /**
16718          * wrapper for event.on - aliased later..  
16719          * Typically use to register a event handler for register:
16720          *
16721          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16722          *
16723          */
16724     on : false
16725    
16726     
16727     
16728 });
16729
16730 Roo.XComponent.event = new Roo.util.Observable({
16731                 events : { 
16732                         /**
16733                          * @event register
16734                          * Fires when an Component is registered,
16735                          * set the disable property on the Component to stop registration.
16736                          * @param {Roo.XComponent} c the component being registerd.
16737                          * 
16738                          */
16739                         'register' : true,
16740             /**
16741                          * @event beforebuild
16742                          * Fires before each Component is built
16743                          * can be used to apply permissions.
16744                          * @param {Roo.XComponent} c the component being registerd.
16745                          * 
16746                          */
16747                         'beforebuild' : true,
16748                         /**
16749                          * @event buildcomplete
16750                          * Fires on the top level element when all elements have been built
16751                          * @param {Roo.XComponent} the top level component.
16752                          */
16753                         'buildcomplete' : true
16754                         
16755                 }
16756 });
16757
16758 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16759  //
16760  /**
16761  * marked - a markdown parser
16762  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16763  * https://github.com/chjj/marked
16764  */
16765
16766
16767 /**
16768  *
16769  * Roo.Markdown - is a very crude wrapper around marked..
16770  *
16771  * usage:
16772  * 
16773  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16774  * 
16775  * Note: move the sample code to the bottom of this
16776  * file before uncommenting it.
16777  *
16778  */
16779
16780 Roo.Markdown = {};
16781 Roo.Markdown.toHtml = function(text) {
16782     
16783     var c = new Roo.Markdown.marked.setOptions({
16784             renderer: new Roo.Markdown.marked.Renderer(),
16785             gfm: true,
16786             tables: true,
16787             breaks: false,
16788             pedantic: false,
16789             sanitize: false,
16790             smartLists: true,
16791             smartypants: false
16792           });
16793     // A FEW HACKS!!?
16794     
16795     text = text.replace(/\\\n/g,' ');
16796     return Roo.Markdown.marked(text);
16797 };
16798 //
16799 // converter
16800 //
16801 // Wraps all "globals" so that the only thing
16802 // exposed is makeHtml().
16803 //
16804 (function() {
16805     
16806     /**
16807      * Block-Level Grammar
16808      */
16809     
16810     var block = {
16811       newline: /^\n+/,
16812       code: /^( {4}[^\n]+\n*)+/,
16813       fences: noop,
16814       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16815       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16816       nptable: noop,
16817       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16818       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16819       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16820       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16821       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16822       table: noop,
16823       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16824       text: /^[^\n]+/
16825     };
16826     
16827     block.bullet = /(?:[*+-]|\d+\.)/;
16828     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16829     block.item = replace(block.item, 'gm')
16830       (/bull/g, block.bullet)
16831       ();
16832     
16833     block.list = replace(block.list)
16834       (/bull/g, block.bullet)
16835       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16836       ('def', '\\n+(?=' + block.def.source + ')')
16837       ();
16838     
16839     block.blockquote = replace(block.blockquote)
16840       ('def', block.def)
16841       ();
16842     
16843     block._tag = '(?!(?:'
16844       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16845       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16846       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16847     
16848     block.html = replace(block.html)
16849       ('comment', /<!--[\s\S]*?-->/)
16850       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16851       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16852       (/tag/g, block._tag)
16853       ();
16854     
16855     block.paragraph = replace(block.paragraph)
16856       ('hr', block.hr)
16857       ('heading', block.heading)
16858       ('lheading', block.lheading)
16859       ('blockquote', block.blockquote)
16860       ('tag', '<' + block._tag)
16861       ('def', block.def)
16862       ();
16863     
16864     /**
16865      * Normal Block Grammar
16866      */
16867     
16868     block.normal = merge({}, block);
16869     
16870     /**
16871      * GFM Block Grammar
16872      */
16873     
16874     block.gfm = merge({}, block.normal, {
16875       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16876       paragraph: /^/,
16877       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16878     });
16879     
16880     block.gfm.paragraph = replace(block.paragraph)
16881       ('(?!', '(?!'
16882         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16883         + block.list.source.replace('\\1', '\\3') + '|')
16884       ();
16885     
16886     /**
16887      * GFM + Tables Block Grammar
16888      */
16889     
16890     block.tables = merge({}, block.gfm, {
16891       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16892       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16893     });
16894     
16895     /**
16896      * Block Lexer
16897      */
16898     
16899     function Lexer(options) {
16900       this.tokens = [];
16901       this.tokens.links = {};
16902       this.options = options || marked.defaults;
16903       this.rules = block.normal;
16904     
16905       if (this.options.gfm) {
16906         if (this.options.tables) {
16907           this.rules = block.tables;
16908         } else {
16909           this.rules = block.gfm;
16910         }
16911       }
16912     }
16913     
16914     /**
16915      * Expose Block Rules
16916      */
16917     
16918     Lexer.rules = block;
16919     
16920     /**
16921      * Static Lex Method
16922      */
16923     
16924     Lexer.lex = function(src, options) {
16925       var lexer = new Lexer(options);
16926       return lexer.lex(src);
16927     };
16928     
16929     /**
16930      * Preprocessing
16931      */
16932     
16933     Lexer.prototype.lex = function(src) {
16934       src = src
16935         .replace(/\r\n|\r/g, '\n')
16936         .replace(/\t/g, '    ')
16937         .replace(/\u00a0/g, ' ')
16938         .replace(/\u2424/g, '\n');
16939     
16940       return this.token(src, true);
16941     };
16942     
16943     /**
16944      * Lexing
16945      */
16946     
16947     Lexer.prototype.token = function(src, top, bq) {
16948       var src = src.replace(/^ +$/gm, '')
16949         , next
16950         , loose
16951         , cap
16952         , bull
16953         , b
16954         , item
16955         , space
16956         , i
16957         , l;
16958     
16959       while (src) {
16960         // newline
16961         if (cap = this.rules.newline.exec(src)) {
16962           src = src.substring(cap[0].length);
16963           if (cap[0].length > 1) {
16964             this.tokens.push({
16965               type: 'space'
16966             });
16967           }
16968         }
16969     
16970         // code
16971         if (cap = this.rules.code.exec(src)) {
16972           src = src.substring(cap[0].length);
16973           cap = cap[0].replace(/^ {4}/gm, '');
16974           this.tokens.push({
16975             type: 'code',
16976             text: !this.options.pedantic
16977               ? cap.replace(/\n+$/, '')
16978               : cap
16979           });
16980           continue;
16981         }
16982     
16983         // fences (gfm)
16984         if (cap = this.rules.fences.exec(src)) {
16985           src = src.substring(cap[0].length);
16986           this.tokens.push({
16987             type: 'code',
16988             lang: cap[2],
16989             text: cap[3] || ''
16990           });
16991           continue;
16992         }
16993     
16994         // heading
16995         if (cap = this.rules.heading.exec(src)) {
16996           src = src.substring(cap[0].length);
16997           this.tokens.push({
16998             type: 'heading',
16999             depth: cap[1].length,
17000             text: cap[2]
17001           });
17002           continue;
17003         }
17004     
17005         // table no leading pipe (gfm)
17006         if (top && (cap = this.rules.nptable.exec(src))) {
17007           src = src.substring(cap[0].length);
17008     
17009           item = {
17010             type: 'table',
17011             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17012             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17013             cells: cap[3].replace(/\n$/, '').split('\n')
17014           };
17015     
17016           for (i = 0; i < item.align.length; i++) {
17017             if (/^ *-+: *$/.test(item.align[i])) {
17018               item.align[i] = 'right';
17019             } else if (/^ *:-+: *$/.test(item.align[i])) {
17020               item.align[i] = 'center';
17021             } else if (/^ *:-+ *$/.test(item.align[i])) {
17022               item.align[i] = 'left';
17023             } else {
17024               item.align[i] = null;
17025             }
17026           }
17027     
17028           for (i = 0; i < item.cells.length; i++) {
17029             item.cells[i] = item.cells[i].split(/ *\| */);
17030           }
17031     
17032           this.tokens.push(item);
17033     
17034           continue;
17035         }
17036     
17037         // lheading
17038         if (cap = this.rules.lheading.exec(src)) {
17039           src = src.substring(cap[0].length);
17040           this.tokens.push({
17041             type: 'heading',
17042             depth: cap[2] === '=' ? 1 : 2,
17043             text: cap[1]
17044           });
17045           continue;
17046         }
17047     
17048         // hr
17049         if (cap = this.rules.hr.exec(src)) {
17050           src = src.substring(cap[0].length);
17051           this.tokens.push({
17052             type: 'hr'
17053           });
17054           continue;
17055         }
17056     
17057         // blockquote
17058         if (cap = this.rules.blockquote.exec(src)) {
17059           src = src.substring(cap[0].length);
17060     
17061           this.tokens.push({
17062             type: 'blockquote_start'
17063           });
17064     
17065           cap = cap[0].replace(/^ *> ?/gm, '');
17066     
17067           // Pass `top` to keep the current
17068           // "toplevel" state. This is exactly
17069           // how markdown.pl works.
17070           this.token(cap, top, true);
17071     
17072           this.tokens.push({
17073             type: 'blockquote_end'
17074           });
17075     
17076           continue;
17077         }
17078     
17079         // list
17080         if (cap = this.rules.list.exec(src)) {
17081           src = src.substring(cap[0].length);
17082           bull = cap[2];
17083     
17084           this.tokens.push({
17085             type: 'list_start',
17086             ordered: bull.length > 1
17087           });
17088     
17089           // Get each top-level item.
17090           cap = cap[0].match(this.rules.item);
17091     
17092           next = false;
17093           l = cap.length;
17094           i = 0;
17095     
17096           for (; i < l; i++) {
17097             item = cap[i];
17098     
17099             // Remove the list item's bullet
17100             // so it is seen as the next token.
17101             space = item.length;
17102             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17103     
17104             // Outdent whatever the
17105             // list item contains. Hacky.
17106             if (~item.indexOf('\n ')) {
17107               space -= item.length;
17108               item = !this.options.pedantic
17109                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17110                 : item.replace(/^ {1,4}/gm, '');
17111             }
17112     
17113             // Determine whether the next list item belongs here.
17114             // Backpedal if it does not belong in this list.
17115             if (this.options.smartLists && i !== l - 1) {
17116               b = block.bullet.exec(cap[i + 1])[0];
17117               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17118                 src = cap.slice(i + 1).join('\n') + src;
17119                 i = l - 1;
17120               }
17121             }
17122     
17123             // Determine whether item is loose or not.
17124             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17125             // for discount behavior.
17126             loose = next || /\n\n(?!\s*$)/.test(item);
17127             if (i !== l - 1) {
17128               next = item.charAt(item.length - 1) === '\n';
17129               if (!loose) { loose = next; }
17130             }
17131     
17132             this.tokens.push({
17133               type: loose
17134                 ? 'loose_item_start'
17135                 : 'list_item_start'
17136             });
17137     
17138             // Recurse.
17139             this.token(item, false, bq);
17140     
17141             this.tokens.push({
17142               type: 'list_item_end'
17143             });
17144           }
17145     
17146           this.tokens.push({
17147             type: 'list_end'
17148           });
17149     
17150           continue;
17151         }
17152     
17153         // html
17154         if (cap = this.rules.html.exec(src)) {
17155           src = src.substring(cap[0].length);
17156           this.tokens.push({
17157             type: this.options.sanitize
17158               ? 'paragraph'
17159               : 'html',
17160             pre: !this.options.sanitizer
17161               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17162             text: cap[0]
17163           });
17164           continue;
17165         }
17166     
17167         // def
17168         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17169           src = src.substring(cap[0].length);
17170           this.tokens.links[cap[1].toLowerCase()] = {
17171             href: cap[2],
17172             title: cap[3]
17173           };
17174           continue;
17175         }
17176     
17177         // table (gfm)
17178         if (top && (cap = this.rules.table.exec(src))) {
17179           src = src.substring(cap[0].length);
17180     
17181           item = {
17182             type: 'table',
17183             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17184             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17185             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17186           };
17187     
17188           for (i = 0; i < item.align.length; i++) {
17189             if (/^ *-+: *$/.test(item.align[i])) {
17190               item.align[i] = 'right';
17191             } else if (/^ *:-+: *$/.test(item.align[i])) {
17192               item.align[i] = 'center';
17193             } else if (/^ *:-+ *$/.test(item.align[i])) {
17194               item.align[i] = 'left';
17195             } else {
17196               item.align[i] = null;
17197             }
17198           }
17199     
17200           for (i = 0; i < item.cells.length; i++) {
17201             item.cells[i] = item.cells[i]
17202               .replace(/^ *\| *| *\| *$/g, '')
17203               .split(/ *\| */);
17204           }
17205     
17206           this.tokens.push(item);
17207     
17208           continue;
17209         }
17210     
17211         // top-level paragraph
17212         if (top && (cap = this.rules.paragraph.exec(src))) {
17213           src = src.substring(cap[0].length);
17214           this.tokens.push({
17215             type: 'paragraph',
17216             text: cap[1].charAt(cap[1].length - 1) === '\n'
17217               ? cap[1].slice(0, -1)
17218               : cap[1]
17219           });
17220           continue;
17221         }
17222     
17223         // text
17224         if (cap = this.rules.text.exec(src)) {
17225           // Top-level should never reach here.
17226           src = src.substring(cap[0].length);
17227           this.tokens.push({
17228             type: 'text',
17229             text: cap[0]
17230           });
17231           continue;
17232         }
17233     
17234         if (src) {
17235           throw new
17236             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17237         }
17238       }
17239     
17240       return this.tokens;
17241     };
17242     
17243     /**
17244      * Inline-Level Grammar
17245      */
17246     
17247     var inline = {
17248       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17249       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17250       url: noop,
17251       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17252       link: /^!?\[(inside)\]\(href\)/,
17253       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17254       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17255       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17256       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17257       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17258       br: /^ {2,}\n(?!\s*$)/,
17259       del: noop,
17260       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17261     };
17262     
17263     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17264     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17265     
17266     inline.link = replace(inline.link)
17267       ('inside', inline._inside)
17268       ('href', inline._href)
17269       ();
17270     
17271     inline.reflink = replace(inline.reflink)
17272       ('inside', inline._inside)
17273       ();
17274     
17275     /**
17276      * Normal Inline Grammar
17277      */
17278     
17279     inline.normal = merge({}, inline);
17280     
17281     /**
17282      * Pedantic Inline Grammar
17283      */
17284     
17285     inline.pedantic = merge({}, inline.normal, {
17286       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17287       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17288     });
17289     
17290     /**
17291      * GFM Inline Grammar
17292      */
17293     
17294     inline.gfm = merge({}, inline.normal, {
17295       escape: replace(inline.escape)('])', '~|])')(),
17296       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17297       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17298       text: replace(inline.text)
17299         (']|', '~]|')
17300         ('|', '|https?://|')
17301         ()
17302     });
17303     
17304     /**
17305      * GFM + Line Breaks Inline Grammar
17306      */
17307     
17308     inline.breaks = merge({}, inline.gfm, {
17309       br: replace(inline.br)('{2,}', '*')(),
17310       text: replace(inline.gfm.text)('{2,}', '*')()
17311     });
17312     
17313     /**
17314      * Inline Lexer & Compiler
17315      */
17316     
17317     function InlineLexer(links, options) {
17318       this.options = options || marked.defaults;
17319       this.links = links;
17320       this.rules = inline.normal;
17321       this.renderer = this.options.renderer || new Renderer;
17322       this.renderer.options = this.options;
17323     
17324       if (!this.links) {
17325         throw new
17326           Error('Tokens array requires a `links` property.');
17327       }
17328     
17329       if (this.options.gfm) {
17330         if (this.options.breaks) {
17331           this.rules = inline.breaks;
17332         } else {
17333           this.rules = inline.gfm;
17334         }
17335       } else if (this.options.pedantic) {
17336         this.rules = inline.pedantic;
17337       }
17338     }
17339     
17340     /**
17341      * Expose Inline Rules
17342      */
17343     
17344     InlineLexer.rules = inline;
17345     
17346     /**
17347      * Static Lexing/Compiling Method
17348      */
17349     
17350     InlineLexer.output = function(src, links, options) {
17351       var inline = new InlineLexer(links, options);
17352       return inline.output(src);
17353     };
17354     
17355     /**
17356      * Lexing/Compiling
17357      */
17358     
17359     InlineLexer.prototype.output = function(src) {
17360       var out = ''
17361         , link
17362         , text
17363         , href
17364         , cap;
17365     
17366       while (src) {
17367         // escape
17368         if (cap = this.rules.escape.exec(src)) {
17369           src = src.substring(cap[0].length);
17370           out += cap[1];
17371           continue;
17372         }
17373     
17374         // autolink
17375         if (cap = this.rules.autolink.exec(src)) {
17376           src = src.substring(cap[0].length);
17377           if (cap[2] === '@') {
17378             text = cap[1].charAt(6) === ':'
17379               ? this.mangle(cap[1].substring(7))
17380               : this.mangle(cap[1]);
17381             href = this.mangle('mailto:') + text;
17382           } else {
17383             text = escape(cap[1]);
17384             href = text;
17385           }
17386           out += this.renderer.link(href, null, text);
17387           continue;
17388         }
17389     
17390         // url (gfm)
17391         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17392           src = src.substring(cap[0].length);
17393           text = escape(cap[1]);
17394           href = text;
17395           out += this.renderer.link(href, null, text);
17396           continue;
17397         }
17398     
17399         // tag
17400         if (cap = this.rules.tag.exec(src)) {
17401           if (!this.inLink && /^<a /i.test(cap[0])) {
17402             this.inLink = true;
17403           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17404             this.inLink = false;
17405           }
17406           src = src.substring(cap[0].length);
17407           out += this.options.sanitize
17408             ? this.options.sanitizer
17409               ? this.options.sanitizer(cap[0])
17410               : escape(cap[0])
17411             : cap[0];
17412           continue;
17413         }
17414     
17415         // link
17416         if (cap = this.rules.link.exec(src)) {
17417           src = src.substring(cap[0].length);
17418           this.inLink = true;
17419           out += this.outputLink(cap, {
17420             href: cap[2],
17421             title: cap[3]
17422           });
17423           this.inLink = false;
17424           continue;
17425         }
17426     
17427         // reflink, nolink
17428         if ((cap = this.rules.reflink.exec(src))
17429             || (cap = this.rules.nolink.exec(src))) {
17430           src = src.substring(cap[0].length);
17431           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17432           link = this.links[link.toLowerCase()];
17433           if (!link || !link.href) {
17434             out += cap[0].charAt(0);
17435             src = cap[0].substring(1) + src;
17436             continue;
17437           }
17438           this.inLink = true;
17439           out += this.outputLink(cap, link);
17440           this.inLink = false;
17441           continue;
17442         }
17443     
17444         // strong
17445         if (cap = this.rules.strong.exec(src)) {
17446           src = src.substring(cap[0].length);
17447           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17448           continue;
17449         }
17450     
17451         // em
17452         if (cap = this.rules.em.exec(src)) {
17453           src = src.substring(cap[0].length);
17454           out += this.renderer.em(this.output(cap[2] || cap[1]));
17455           continue;
17456         }
17457     
17458         // code
17459         if (cap = this.rules.code.exec(src)) {
17460           src = src.substring(cap[0].length);
17461           out += this.renderer.codespan(escape(cap[2], true));
17462           continue;
17463         }
17464     
17465         // br
17466         if (cap = this.rules.br.exec(src)) {
17467           src = src.substring(cap[0].length);
17468           out += this.renderer.br();
17469           continue;
17470         }
17471     
17472         // del (gfm)
17473         if (cap = this.rules.del.exec(src)) {
17474           src = src.substring(cap[0].length);
17475           out += this.renderer.del(this.output(cap[1]));
17476           continue;
17477         }
17478     
17479         // text
17480         if (cap = this.rules.text.exec(src)) {
17481           src = src.substring(cap[0].length);
17482           out += this.renderer.text(escape(this.smartypants(cap[0])));
17483           continue;
17484         }
17485     
17486         if (src) {
17487           throw new
17488             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17489         }
17490       }
17491     
17492       return out;
17493     };
17494     
17495     /**
17496      * Compile Link
17497      */
17498     
17499     InlineLexer.prototype.outputLink = function(cap, link) {
17500       var href = escape(link.href)
17501         , title = link.title ? escape(link.title) : null;
17502     
17503       return cap[0].charAt(0) !== '!'
17504         ? this.renderer.link(href, title, this.output(cap[1]))
17505         : this.renderer.image(href, title, escape(cap[1]));
17506     };
17507     
17508     /**
17509      * Smartypants Transformations
17510      */
17511     
17512     InlineLexer.prototype.smartypants = function(text) {
17513       if (!this.options.smartypants)  { return text; }
17514       return text
17515         // em-dashes
17516         .replace(/---/g, '\u2014')
17517         // en-dashes
17518         .replace(/--/g, '\u2013')
17519         // opening singles
17520         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17521         // closing singles & apostrophes
17522         .replace(/'/g, '\u2019')
17523         // opening doubles
17524         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17525         // closing doubles
17526         .replace(/"/g, '\u201d')
17527         // ellipses
17528         .replace(/\.{3}/g, '\u2026');
17529     };
17530     
17531     /**
17532      * Mangle Links
17533      */
17534     
17535     InlineLexer.prototype.mangle = function(text) {
17536       if (!this.options.mangle) { return text; }
17537       var out = ''
17538         , l = text.length
17539         , i = 0
17540         , ch;
17541     
17542       for (; i < l; i++) {
17543         ch = text.charCodeAt(i);
17544         if (Math.random() > 0.5) {
17545           ch = 'x' + ch.toString(16);
17546         }
17547         out += '&#' + ch + ';';
17548       }
17549     
17550       return out;
17551     };
17552     
17553     /**
17554      * Renderer
17555      */
17556     
17557     function Renderer(options) {
17558       this.options = options || {};
17559     }
17560     
17561     Renderer.prototype.code = function(code, lang, escaped) {
17562       if (this.options.highlight) {
17563         var out = this.options.highlight(code, lang);
17564         if (out != null && out !== code) {
17565           escaped = true;
17566           code = out;
17567         }
17568       } else {
17569             // hack!!! - it's already escapeD?
17570             escaped = true;
17571       }
17572     
17573       if (!lang) {
17574         return '<pre><code>'
17575           + (escaped ? code : escape(code, true))
17576           + '\n</code></pre>';
17577       }
17578     
17579       return '<pre><code class="'
17580         + this.options.langPrefix
17581         + escape(lang, true)
17582         + '">'
17583         + (escaped ? code : escape(code, true))
17584         + '\n</code></pre>\n';
17585     };
17586     
17587     Renderer.prototype.blockquote = function(quote) {
17588       return '<blockquote>\n' + quote + '</blockquote>\n';
17589     };
17590     
17591     Renderer.prototype.html = function(html) {
17592       return html;
17593     };
17594     
17595     Renderer.prototype.heading = function(text, level, raw) {
17596       return '<h'
17597         + level
17598         + ' id="'
17599         + this.options.headerPrefix
17600         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17601         + '">'
17602         + text
17603         + '</h'
17604         + level
17605         + '>\n';
17606     };
17607     
17608     Renderer.prototype.hr = function() {
17609       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17610     };
17611     
17612     Renderer.prototype.list = function(body, ordered) {
17613       var type = ordered ? 'ol' : 'ul';
17614       return '<' + type + '>\n' + body + '</' + type + '>\n';
17615     };
17616     
17617     Renderer.prototype.listitem = function(text) {
17618       return '<li>' + text + '</li>\n';
17619     };
17620     
17621     Renderer.prototype.paragraph = function(text) {
17622       return '<p>' + text + '</p>\n';
17623     };
17624     
17625     Renderer.prototype.table = function(header, body) {
17626       return '<table class="table table-striped">\n'
17627         + '<thead>\n'
17628         + header
17629         + '</thead>\n'
17630         + '<tbody>\n'
17631         + body
17632         + '</tbody>\n'
17633         + '</table>\n';
17634     };
17635     
17636     Renderer.prototype.tablerow = function(content) {
17637       return '<tr>\n' + content + '</tr>\n';
17638     };
17639     
17640     Renderer.prototype.tablecell = function(content, flags) {
17641       var type = flags.header ? 'th' : 'td';
17642       var tag = flags.align
17643         ? '<' + type + ' style="text-align:' + flags.align + '">'
17644         : '<' + type + '>';
17645       return tag + content + '</' + type + '>\n';
17646     };
17647     
17648     // span level renderer
17649     Renderer.prototype.strong = function(text) {
17650       return '<strong>' + text + '</strong>';
17651     };
17652     
17653     Renderer.prototype.em = function(text) {
17654       return '<em>' + text + '</em>';
17655     };
17656     
17657     Renderer.prototype.codespan = function(text) {
17658       return '<code>' + text + '</code>';
17659     };
17660     
17661     Renderer.prototype.br = function() {
17662       return this.options.xhtml ? '<br/>' : '<br>';
17663     };
17664     
17665     Renderer.prototype.del = function(text) {
17666       return '<del>' + text + '</del>';
17667     };
17668     
17669     Renderer.prototype.link = function(href, title, text) {
17670       if (this.options.sanitize) {
17671         try {
17672           var prot = decodeURIComponent(unescape(href))
17673             .replace(/[^\w:]/g, '')
17674             .toLowerCase();
17675         } catch (e) {
17676           return '';
17677         }
17678         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17679           return '';
17680         }
17681       }
17682       var out = '<a href="' + href + '"';
17683       if (title) {
17684         out += ' title="' + title + '"';
17685       }
17686       out += '>' + text + '</a>';
17687       return out;
17688     };
17689     
17690     Renderer.prototype.image = function(href, title, text) {
17691       var out = '<img src="' + href + '" alt="' + text + '"';
17692       if (title) {
17693         out += ' title="' + title + '"';
17694       }
17695       out += this.options.xhtml ? '/>' : '>';
17696       return out;
17697     };
17698     
17699     Renderer.prototype.text = function(text) {
17700       return text;
17701     };
17702     
17703     /**
17704      * Parsing & Compiling
17705      */
17706     
17707     function Parser(options) {
17708       this.tokens = [];
17709       this.token = null;
17710       this.options = options || marked.defaults;
17711       this.options.renderer = this.options.renderer || new Renderer;
17712       this.renderer = this.options.renderer;
17713       this.renderer.options = this.options;
17714     }
17715     
17716     /**
17717      * Static Parse Method
17718      */
17719     
17720     Parser.parse = function(src, options, renderer) {
17721       var parser = new Parser(options, renderer);
17722       return parser.parse(src);
17723     };
17724     
17725     /**
17726      * Parse Loop
17727      */
17728     
17729     Parser.prototype.parse = function(src) {
17730       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17731       this.tokens = src.reverse();
17732     
17733       var out = '';
17734       while (this.next()) {
17735         out += this.tok();
17736       }
17737     
17738       return out;
17739     };
17740     
17741     /**
17742      * Next Token
17743      */
17744     
17745     Parser.prototype.next = function() {
17746       return this.token = this.tokens.pop();
17747     };
17748     
17749     /**
17750      * Preview Next Token
17751      */
17752     
17753     Parser.prototype.peek = function() {
17754       return this.tokens[this.tokens.length - 1] || 0;
17755     };
17756     
17757     /**
17758      * Parse Text Tokens
17759      */
17760     
17761     Parser.prototype.parseText = function() {
17762       var body = this.token.text;
17763     
17764       while (this.peek().type === 'text') {
17765         body += '\n' + this.next().text;
17766       }
17767     
17768       return this.inline.output(body);
17769     };
17770     
17771     /**
17772      * Parse Current Token
17773      */
17774     
17775     Parser.prototype.tok = function() {
17776       switch (this.token.type) {
17777         case 'space': {
17778           return '';
17779         }
17780         case 'hr': {
17781           return this.renderer.hr();
17782         }
17783         case 'heading': {
17784           return this.renderer.heading(
17785             this.inline.output(this.token.text),
17786             this.token.depth,
17787             this.token.text);
17788         }
17789         case 'code': {
17790           return this.renderer.code(this.token.text,
17791             this.token.lang,
17792             this.token.escaped);
17793         }
17794         case 'table': {
17795           var header = ''
17796             , body = ''
17797             , i
17798             , row
17799             , cell
17800             , flags
17801             , j;
17802     
17803           // header
17804           cell = '';
17805           for (i = 0; i < this.token.header.length; i++) {
17806             flags = { header: true, align: this.token.align[i] };
17807             cell += this.renderer.tablecell(
17808               this.inline.output(this.token.header[i]),
17809               { header: true, align: this.token.align[i] }
17810             );
17811           }
17812           header += this.renderer.tablerow(cell);
17813     
17814           for (i = 0; i < this.token.cells.length; i++) {
17815             row = this.token.cells[i];
17816     
17817             cell = '';
17818             for (j = 0; j < row.length; j++) {
17819               cell += this.renderer.tablecell(
17820                 this.inline.output(row[j]),
17821                 { header: false, align: this.token.align[j] }
17822               );
17823             }
17824     
17825             body += this.renderer.tablerow(cell);
17826           }
17827           return this.renderer.table(header, body);
17828         }
17829         case 'blockquote_start': {
17830           var body = '';
17831     
17832           while (this.next().type !== 'blockquote_end') {
17833             body += this.tok();
17834           }
17835     
17836           return this.renderer.blockquote(body);
17837         }
17838         case 'list_start': {
17839           var body = ''
17840             , ordered = this.token.ordered;
17841     
17842           while (this.next().type !== 'list_end') {
17843             body += this.tok();
17844           }
17845     
17846           return this.renderer.list(body, ordered);
17847         }
17848         case 'list_item_start': {
17849           var body = '';
17850     
17851           while (this.next().type !== 'list_item_end') {
17852             body += this.token.type === 'text'
17853               ? this.parseText()
17854               : this.tok();
17855           }
17856     
17857           return this.renderer.listitem(body);
17858         }
17859         case 'loose_item_start': {
17860           var body = '';
17861     
17862           while (this.next().type !== 'list_item_end') {
17863             body += this.tok();
17864           }
17865     
17866           return this.renderer.listitem(body);
17867         }
17868         case 'html': {
17869           var html = !this.token.pre && !this.options.pedantic
17870             ? this.inline.output(this.token.text)
17871             : this.token.text;
17872           return this.renderer.html(html);
17873         }
17874         case 'paragraph': {
17875           return this.renderer.paragraph(this.inline.output(this.token.text));
17876         }
17877         case 'text': {
17878           return this.renderer.paragraph(this.parseText());
17879         }
17880       }
17881     };
17882     
17883     /**
17884      * Helpers
17885      */
17886     
17887     function escape(html, encode) {
17888       return html
17889         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17890         .replace(/</g, '&lt;')
17891         .replace(/>/g, '&gt;')
17892         .replace(/"/g, '&quot;')
17893         .replace(/'/g, '&#39;');
17894     }
17895     
17896     function unescape(html) {
17897         // explicitly match decimal, hex, and named HTML entities 
17898       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17899         n = n.toLowerCase();
17900         if (n === 'colon') { return ':'; }
17901         if (n.charAt(0) === '#') {
17902           return n.charAt(1) === 'x'
17903             ? String.fromCharCode(parseInt(n.substring(2), 16))
17904             : String.fromCharCode(+n.substring(1));
17905         }
17906         return '';
17907       });
17908     }
17909     
17910     function replace(regex, opt) {
17911       regex = regex.source;
17912       opt = opt || '';
17913       return function self(name, val) {
17914         if (!name) { return new RegExp(regex, opt); }
17915         val = val.source || val;
17916         val = val.replace(/(^|[^\[])\^/g, '$1');
17917         regex = regex.replace(name, val);
17918         return self;
17919       };
17920     }
17921     
17922     function noop() {}
17923     noop.exec = noop;
17924     
17925     function merge(obj) {
17926       var i = 1
17927         , target
17928         , key;
17929     
17930       for (; i < arguments.length; i++) {
17931         target = arguments[i];
17932         for (key in target) {
17933           if (Object.prototype.hasOwnProperty.call(target, key)) {
17934             obj[key] = target[key];
17935           }
17936         }
17937       }
17938     
17939       return obj;
17940     }
17941     
17942     
17943     /**
17944      * Marked
17945      */
17946     
17947     function marked(src, opt, callback) {
17948       if (callback || typeof opt === 'function') {
17949         if (!callback) {
17950           callback = opt;
17951           opt = null;
17952         }
17953     
17954         opt = merge({}, marked.defaults, opt || {});
17955     
17956         var highlight = opt.highlight
17957           , tokens
17958           , pending
17959           , i = 0;
17960     
17961         try {
17962           tokens = Lexer.lex(src, opt)
17963         } catch (e) {
17964           return callback(e);
17965         }
17966     
17967         pending = tokens.length;
17968     
17969         var done = function(err) {
17970           if (err) {
17971             opt.highlight = highlight;
17972             return callback(err);
17973           }
17974     
17975           var out;
17976     
17977           try {
17978             out = Parser.parse(tokens, opt);
17979           } catch (e) {
17980             err = e;
17981           }
17982     
17983           opt.highlight = highlight;
17984     
17985           return err
17986             ? callback(err)
17987             : callback(null, out);
17988         };
17989     
17990         if (!highlight || highlight.length < 3) {
17991           return done();
17992         }
17993     
17994         delete opt.highlight;
17995     
17996         if (!pending) { return done(); }
17997     
17998         for (; i < tokens.length; i++) {
17999           (function(token) {
18000             if (token.type !== 'code') {
18001               return --pending || done();
18002             }
18003             return highlight(token.text, token.lang, function(err, code) {
18004               if (err) { return done(err); }
18005               if (code == null || code === token.text) {
18006                 return --pending || done();
18007               }
18008               token.text = code;
18009               token.escaped = true;
18010               --pending || done();
18011             });
18012           })(tokens[i]);
18013         }
18014     
18015         return;
18016       }
18017       try {
18018         if (opt) { opt = merge({}, marked.defaults, opt); }
18019         return Parser.parse(Lexer.lex(src, opt), opt);
18020       } catch (e) {
18021         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18022         if ((opt || marked.defaults).silent) {
18023           return '<p>An error occured:</p><pre>'
18024             + escape(e.message + '', true)
18025             + '</pre>';
18026         }
18027         throw e;
18028       }
18029     }
18030     
18031     /**
18032      * Options
18033      */
18034     
18035     marked.options =
18036     marked.setOptions = function(opt) {
18037       merge(marked.defaults, opt);
18038       return marked;
18039     };
18040     
18041     marked.defaults = {
18042       gfm: true,
18043       tables: true,
18044       breaks: false,
18045       pedantic: false,
18046       sanitize: false,
18047       sanitizer: null,
18048       mangle: true,
18049       smartLists: false,
18050       silent: false,
18051       highlight: null,
18052       langPrefix: 'lang-',
18053       smartypants: false,
18054       headerPrefix: '',
18055       renderer: new Renderer,
18056       xhtml: false
18057     };
18058     
18059     /**
18060      * Expose
18061      */
18062     
18063     marked.Parser = Parser;
18064     marked.parser = Parser.parse;
18065     
18066     marked.Renderer = Renderer;
18067     
18068     marked.Lexer = Lexer;
18069     marked.lexer = Lexer.lex;
18070     
18071     marked.InlineLexer = InlineLexer;
18072     marked.inlineLexer = InlineLexer.output;
18073     
18074     marked.parse = marked;
18075     
18076     Roo.Markdown.marked = marked;
18077
18078 })();/*
18079  * Based on:
18080  * Ext JS Library 1.1.1
18081  * Copyright(c) 2006-2007, Ext JS, LLC.
18082  *
18083  * Originally Released Under LGPL - original licence link has changed is not relivant.
18084  *
18085  * Fork - LGPL
18086  * <script type="text/javascript">
18087  */
18088
18089
18090
18091 /*
18092  * These classes are derivatives of the similarly named classes in the YUI Library.
18093  * The original license:
18094  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18095  * Code licensed under the BSD License:
18096  * http://developer.yahoo.net/yui/license.txt
18097  */
18098
18099 (function() {
18100
18101 var Event=Roo.EventManager;
18102 var Dom=Roo.lib.Dom;
18103
18104 /**
18105  * @class Roo.dd.DragDrop
18106  * @extends Roo.util.Observable
18107  * Defines the interface and base operation of items that that can be
18108  * dragged or can be drop targets.  It was designed to be extended, overriding
18109  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18110  * Up to three html elements can be associated with a DragDrop instance:
18111  * <ul>
18112  * <li>linked element: the element that is passed into the constructor.
18113  * This is the element which defines the boundaries for interaction with
18114  * other DragDrop objects.</li>
18115  * <li>handle element(s): The drag operation only occurs if the element that
18116  * was clicked matches a handle element.  By default this is the linked
18117  * element, but there are times that you will want only a portion of the
18118  * linked element to initiate the drag operation, and the setHandleElId()
18119  * method provides a way to define this.</li>
18120  * <li>drag element: this represents the element that would be moved along
18121  * with the cursor during a drag operation.  By default, this is the linked
18122  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18123  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18124  * </li>
18125  * </ul>
18126  * This class should not be instantiated until the onload event to ensure that
18127  * the associated elements are available.
18128  * The following would define a DragDrop obj that would interact with any
18129  * other DragDrop obj in the "group1" group:
18130  * <pre>
18131  *  dd = new Roo.dd.DragDrop("div1", "group1");
18132  * </pre>
18133  * Since none of the event handlers have been implemented, nothing would
18134  * actually happen if you were to run the code above.  Normally you would
18135  * override this class or one of the default implementations, but you can
18136  * also override the methods you want on an instance of the class...
18137  * <pre>
18138  *  dd.onDragDrop = function(e, id) {
18139  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18140  *  }
18141  * </pre>
18142  * @constructor
18143  * @param {String} id of the element that is linked to this instance
18144  * @param {String} sGroup the group of related DragDrop objects
18145  * @param {object} config an object containing configurable attributes
18146  *                Valid properties for DragDrop:
18147  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18148  */
18149 Roo.dd.DragDrop = function(id, sGroup, config) {
18150     if (id) {
18151         this.init(id, sGroup, config);
18152     }
18153     
18154 };
18155
18156 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18157
18158     /**
18159      * The id of the element associated with this object.  This is what we
18160      * refer to as the "linked element" because the size and position of
18161      * this element is used to determine when the drag and drop objects have
18162      * interacted.
18163      * @property id
18164      * @type String
18165      */
18166     id: null,
18167
18168     /**
18169      * Configuration attributes passed into the constructor
18170      * @property config
18171      * @type object
18172      */
18173     config: null,
18174
18175     /**
18176      * The id of the element that will be dragged.  By default this is same
18177      * as the linked element , but could be changed to another element. Ex:
18178      * Roo.dd.DDProxy
18179      * @property dragElId
18180      * @type String
18181      * @private
18182      */
18183     dragElId: null,
18184
18185     /**
18186      * the id of the element that initiates the drag operation.  By default
18187      * this is the linked element, but could be changed to be a child of this
18188      * element.  This lets us do things like only starting the drag when the
18189      * header element within the linked html element is clicked.
18190      * @property handleElId
18191      * @type String
18192      * @private
18193      */
18194     handleElId: null,
18195
18196     /**
18197      * An associative array of HTML tags that will be ignored if clicked.
18198      * @property invalidHandleTypes
18199      * @type {string: string}
18200      */
18201     invalidHandleTypes: null,
18202
18203     /**
18204      * An associative array of ids for elements that will be ignored if clicked
18205      * @property invalidHandleIds
18206      * @type {string: string}
18207      */
18208     invalidHandleIds: null,
18209
18210     /**
18211      * An indexted array of css class names for elements that will be ignored
18212      * if clicked.
18213      * @property invalidHandleClasses
18214      * @type string[]
18215      */
18216     invalidHandleClasses: null,
18217
18218     /**
18219      * The linked element's absolute X position at the time the drag was
18220      * started
18221      * @property startPageX
18222      * @type int
18223      * @private
18224      */
18225     startPageX: 0,
18226
18227     /**
18228      * The linked element's absolute X position at the time the drag was
18229      * started
18230      * @property startPageY
18231      * @type int
18232      * @private
18233      */
18234     startPageY: 0,
18235
18236     /**
18237      * The group defines a logical collection of DragDrop objects that are
18238      * related.  Instances only get events when interacting with other
18239      * DragDrop object in the same group.  This lets us define multiple
18240      * groups using a single DragDrop subclass if we want.
18241      * @property groups
18242      * @type {string: string}
18243      */
18244     groups: null,
18245
18246     /**
18247      * Individual drag/drop instances can be locked.  This will prevent
18248      * onmousedown start drag.
18249      * @property locked
18250      * @type boolean
18251      * @private
18252      */
18253     locked: false,
18254
18255     /**
18256      * Lock this instance
18257      * @method lock
18258      */
18259     lock: function() { this.locked = true; },
18260
18261     /**
18262      * Unlock this instace
18263      * @method unlock
18264      */
18265     unlock: function() { this.locked = false; },
18266
18267     /**
18268      * By default, all insances can be a drop target.  This can be disabled by
18269      * setting isTarget to false.
18270      * @method isTarget
18271      * @type boolean
18272      */
18273     isTarget: true,
18274
18275     /**
18276      * The padding configured for this drag and drop object for calculating
18277      * the drop zone intersection with this object.
18278      * @method padding
18279      * @type int[]
18280      */
18281     padding: null,
18282
18283     /**
18284      * Cached reference to the linked element
18285      * @property _domRef
18286      * @private
18287      */
18288     _domRef: null,
18289
18290     /**
18291      * Internal typeof flag
18292      * @property __ygDragDrop
18293      * @private
18294      */
18295     __ygDragDrop: true,
18296
18297     /**
18298      * Set to true when horizontal contraints are applied
18299      * @property constrainX
18300      * @type boolean
18301      * @private
18302      */
18303     constrainX: false,
18304
18305     /**
18306      * Set to true when vertical contraints are applied
18307      * @property constrainY
18308      * @type boolean
18309      * @private
18310      */
18311     constrainY: false,
18312
18313     /**
18314      * The left constraint
18315      * @property minX
18316      * @type int
18317      * @private
18318      */
18319     minX: 0,
18320
18321     /**
18322      * The right constraint
18323      * @property maxX
18324      * @type int
18325      * @private
18326      */
18327     maxX: 0,
18328
18329     /**
18330      * The up constraint
18331      * @property minY
18332      * @type int
18333      * @type int
18334      * @private
18335      */
18336     minY: 0,
18337
18338     /**
18339      * The down constraint
18340      * @property maxY
18341      * @type int
18342      * @private
18343      */
18344     maxY: 0,
18345
18346     /**
18347      * Maintain offsets when we resetconstraints.  Set to true when you want
18348      * the position of the element relative to its parent to stay the same
18349      * when the page changes
18350      *
18351      * @property maintainOffset
18352      * @type boolean
18353      */
18354     maintainOffset: false,
18355
18356     /**
18357      * Array of pixel locations the element will snap to if we specified a
18358      * horizontal graduation/interval.  This array is generated automatically
18359      * when you define a tick interval.
18360      * @property xTicks
18361      * @type int[]
18362      */
18363     xTicks: null,
18364
18365     /**
18366      * Array of pixel locations the element will snap to if we specified a
18367      * vertical graduation/interval.  This array is generated automatically
18368      * when you define a tick interval.
18369      * @property yTicks
18370      * @type int[]
18371      */
18372     yTicks: null,
18373
18374     /**
18375      * By default the drag and drop instance will only respond to the primary
18376      * button click (left button for a right-handed mouse).  Set to true to
18377      * allow drag and drop to start with any mouse click that is propogated
18378      * by the browser
18379      * @property primaryButtonOnly
18380      * @type boolean
18381      */
18382     primaryButtonOnly: true,
18383
18384     /**
18385      * The availabe property is false until the linked dom element is accessible.
18386      * @property available
18387      * @type boolean
18388      */
18389     available: false,
18390
18391     /**
18392      * By default, drags can only be initiated if the mousedown occurs in the
18393      * region the linked element is.  This is done in part to work around a
18394      * bug in some browsers that mis-report the mousedown if the previous
18395      * mouseup happened outside of the window.  This property is set to true
18396      * if outer handles are defined.
18397      *
18398      * @property hasOuterHandles
18399      * @type boolean
18400      * @default false
18401      */
18402     hasOuterHandles: false,
18403
18404     /**
18405      * Code that executes immediately before the startDrag event
18406      * @method b4StartDrag
18407      * @private
18408      */
18409     b4StartDrag: function(x, y) { },
18410
18411     /**
18412      * Abstract method called after a drag/drop object is clicked
18413      * and the drag or mousedown time thresholds have beeen met.
18414      * @method startDrag
18415      * @param {int} X click location
18416      * @param {int} Y click location
18417      */
18418     startDrag: function(x, y) { /* override this */ },
18419
18420     /**
18421      * Code that executes immediately before the onDrag event
18422      * @method b4Drag
18423      * @private
18424      */
18425     b4Drag: function(e) { },
18426
18427     /**
18428      * Abstract method called during the onMouseMove event while dragging an
18429      * object.
18430      * @method onDrag
18431      * @param {Event} e the mousemove event
18432      */
18433     onDrag: function(e) { /* override this */ },
18434
18435     /**
18436      * Abstract method called when this element fist begins hovering over
18437      * another DragDrop obj
18438      * @method onDragEnter
18439      * @param {Event} e the mousemove event
18440      * @param {String|DragDrop[]} id In POINT mode, the element
18441      * id this is hovering over.  In INTERSECT mode, an array of one or more
18442      * dragdrop items being hovered over.
18443      */
18444     onDragEnter: function(e, id) { /* override this */ },
18445
18446     /**
18447      * Code that executes immediately before the onDragOver event
18448      * @method b4DragOver
18449      * @private
18450      */
18451     b4DragOver: function(e) { },
18452
18453     /**
18454      * Abstract method called when this element is hovering over another
18455      * DragDrop obj
18456      * @method onDragOver
18457      * @param {Event} e the mousemove event
18458      * @param {String|DragDrop[]} id In POINT mode, the element
18459      * id this is hovering over.  In INTERSECT mode, an array of dd items
18460      * being hovered over.
18461      */
18462     onDragOver: function(e, id) { /* override this */ },
18463
18464     /**
18465      * Code that executes immediately before the onDragOut event
18466      * @method b4DragOut
18467      * @private
18468      */
18469     b4DragOut: function(e) { },
18470
18471     /**
18472      * Abstract method called when we are no longer hovering over an element
18473      * @method onDragOut
18474      * @param {Event} e the mousemove event
18475      * @param {String|DragDrop[]} id In POINT mode, the element
18476      * id this was hovering over.  In INTERSECT mode, an array of dd items
18477      * that the mouse is no longer over.
18478      */
18479     onDragOut: function(e, id) { /* override this */ },
18480
18481     /**
18482      * Code that executes immediately before the onDragDrop event
18483      * @method b4DragDrop
18484      * @private
18485      */
18486     b4DragDrop: function(e) { },
18487
18488     /**
18489      * Abstract method called when this item is dropped on another DragDrop
18490      * obj
18491      * @method onDragDrop
18492      * @param {Event} e the mouseup event
18493      * @param {String|DragDrop[]} id In POINT mode, the element
18494      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18495      * was dropped on.
18496      */
18497     onDragDrop: function(e, id) { /* override this */ },
18498
18499     /**
18500      * Abstract method called when this item is dropped on an area with no
18501      * drop target
18502      * @method onInvalidDrop
18503      * @param {Event} e the mouseup event
18504      */
18505     onInvalidDrop: function(e) { /* override this */ },
18506
18507     /**
18508      * Code that executes immediately before the endDrag event
18509      * @method b4EndDrag
18510      * @private
18511      */
18512     b4EndDrag: function(e) { },
18513
18514     /**
18515      * Fired when we are done dragging the object
18516      * @method endDrag
18517      * @param {Event} e the mouseup event
18518      */
18519     endDrag: function(e) { /* override this */ },
18520
18521     /**
18522      * Code executed immediately before the onMouseDown event
18523      * @method b4MouseDown
18524      * @param {Event} e the mousedown event
18525      * @private
18526      */
18527     b4MouseDown: function(e) {  },
18528
18529     /**
18530      * Event handler that fires when a drag/drop obj gets a mousedown
18531      * @method onMouseDown
18532      * @param {Event} e the mousedown event
18533      */
18534     onMouseDown: function(e) { /* override this */ },
18535
18536     /**
18537      * Event handler that fires when a drag/drop obj gets a mouseup
18538      * @method onMouseUp
18539      * @param {Event} e the mouseup event
18540      */
18541     onMouseUp: function(e) { /* override this */ },
18542
18543     /**
18544      * Override the onAvailable method to do what is needed after the initial
18545      * position was determined.
18546      * @method onAvailable
18547      */
18548     onAvailable: function () {
18549     },
18550
18551     /*
18552      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18553      * @type Object
18554      */
18555     defaultPadding : {left:0, right:0, top:0, bottom:0},
18556
18557     /*
18558      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18559  *
18560  * Usage:
18561  <pre><code>
18562  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18563                 { dragElId: "existingProxyDiv" });
18564  dd.startDrag = function(){
18565      this.constrainTo("parent-id");
18566  };
18567  </code></pre>
18568  * Or you can initalize it using the {@link Roo.Element} object:
18569  <pre><code>
18570  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18571      startDrag : function(){
18572          this.constrainTo("parent-id");
18573      }
18574  });
18575  </code></pre>
18576      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18577      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18578      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18579      * an object containing the sides to pad. For example: {right:10, bottom:10}
18580      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18581      */
18582     constrainTo : function(constrainTo, pad, inContent){
18583         if(typeof pad == "number"){
18584             pad = {left: pad, right:pad, top:pad, bottom:pad};
18585         }
18586         pad = pad || this.defaultPadding;
18587         var b = Roo.get(this.getEl()).getBox();
18588         var ce = Roo.get(constrainTo);
18589         var s = ce.getScroll();
18590         var c, cd = ce.dom;
18591         if(cd == document.body){
18592             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18593         }else{
18594             xy = ce.getXY();
18595             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18596         }
18597
18598
18599         var topSpace = b.y - c.y;
18600         var leftSpace = b.x - c.x;
18601
18602         this.resetConstraints();
18603         this.setXConstraint(leftSpace - (pad.left||0), // left
18604                 c.width - leftSpace - b.width - (pad.right||0) //right
18605         );
18606         this.setYConstraint(topSpace - (pad.top||0), //top
18607                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18608         );
18609     },
18610
18611     /**
18612      * Returns a reference to the linked element
18613      * @method getEl
18614      * @return {HTMLElement} the html element
18615      */
18616     getEl: function() {
18617         if (!this._domRef) {
18618             this._domRef = Roo.getDom(this.id);
18619         }
18620
18621         return this._domRef;
18622     },
18623
18624     /**
18625      * Returns a reference to the actual element to drag.  By default this is
18626      * the same as the html element, but it can be assigned to another
18627      * element. An example of this can be found in Roo.dd.DDProxy
18628      * @method getDragEl
18629      * @return {HTMLElement} the html element
18630      */
18631     getDragEl: function() {
18632         return Roo.getDom(this.dragElId);
18633     },
18634
18635     /**
18636      * Sets up the DragDrop object.  Must be called in the constructor of any
18637      * Roo.dd.DragDrop subclass
18638      * @method init
18639      * @param id the id of the linked element
18640      * @param {String} sGroup the group of related items
18641      * @param {object} config configuration attributes
18642      */
18643     init: function(id, sGroup, config) {
18644         this.initTarget(id, sGroup, config);
18645         if (!Roo.isTouch) {
18646             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18647         }
18648         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18649         // Event.on(this.id, "selectstart", Event.preventDefault);
18650     },
18651
18652     /**
18653      * Initializes Targeting functionality only... the object does not
18654      * get a mousedown handler.
18655      * @method initTarget
18656      * @param id the id of the linked element
18657      * @param {String} sGroup the group of related items
18658      * @param {object} config configuration attributes
18659      */
18660     initTarget: function(id, sGroup, config) {
18661
18662         // configuration attributes
18663         this.config = config || {};
18664
18665         // create a local reference to the drag and drop manager
18666         this.DDM = Roo.dd.DDM;
18667         // initialize the groups array
18668         this.groups = {};
18669
18670         // assume that we have an element reference instead of an id if the
18671         // parameter is not a string
18672         if (typeof id !== "string") {
18673             id = Roo.id(id);
18674         }
18675
18676         // set the id
18677         this.id = id;
18678
18679         // add to an interaction group
18680         this.addToGroup((sGroup) ? sGroup : "default");
18681
18682         // We don't want to register this as the handle with the manager
18683         // so we just set the id rather than calling the setter.
18684         this.handleElId = id;
18685
18686         // the linked element is the element that gets dragged by default
18687         this.setDragElId(id);
18688
18689         // by default, clicked anchors will not start drag operations.
18690         this.invalidHandleTypes = { A: "A" };
18691         this.invalidHandleIds = {};
18692         this.invalidHandleClasses = [];
18693
18694         this.applyConfig();
18695
18696         this.handleOnAvailable();
18697     },
18698
18699     /**
18700      * Applies the configuration parameters that were passed into the constructor.
18701      * This is supposed to happen at each level through the inheritance chain.  So
18702      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18703      * DragDrop in order to get all of the parameters that are available in
18704      * each object.
18705      * @method applyConfig
18706      */
18707     applyConfig: function() {
18708
18709         // configurable properties:
18710         //    padding, isTarget, maintainOffset, primaryButtonOnly
18711         this.padding           = this.config.padding || [0, 0, 0, 0];
18712         this.isTarget          = (this.config.isTarget !== false);
18713         this.maintainOffset    = (this.config.maintainOffset);
18714         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18715
18716     },
18717
18718     /**
18719      * Executed when the linked element is available
18720      * @method handleOnAvailable
18721      * @private
18722      */
18723     handleOnAvailable: function() {
18724         this.available = true;
18725         this.resetConstraints();
18726         this.onAvailable();
18727     },
18728
18729      /**
18730      * Configures the padding for the target zone in px.  Effectively expands
18731      * (or reduces) the virtual object size for targeting calculations.
18732      * Supports css-style shorthand; if only one parameter is passed, all sides
18733      * will have that padding, and if only two are passed, the top and bottom
18734      * will have the first param, the left and right the second.
18735      * @method setPadding
18736      * @param {int} iTop    Top pad
18737      * @param {int} iRight  Right pad
18738      * @param {int} iBot    Bot pad
18739      * @param {int} iLeft   Left pad
18740      */
18741     setPadding: function(iTop, iRight, iBot, iLeft) {
18742         // this.padding = [iLeft, iRight, iTop, iBot];
18743         if (!iRight && 0 !== iRight) {
18744             this.padding = [iTop, iTop, iTop, iTop];
18745         } else if (!iBot && 0 !== iBot) {
18746             this.padding = [iTop, iRight, iTop, iRight];
18747         } else {
18748             this.padding = [iTop, iRight, iBot, iLeft];
18749         }
18750     },
18751
18752     /**
18753      * Stores the initial placement of the linked element.
18754      * @method setInitialPosition
18755      * @param {int} diffX   the X offset, default 0
18756      * @param {int} diffY   the Y offset, default 0
18757      */
18758     setInitPosition: function(diffX, diffY) {
18759         var el = this.getEl();
18760
18761         if (!this.DDM.verifyEl(el)) {
18762             return;
18763         }
18764
18765         var dx = diffX || 0;
18766         var dy = diffY || 0;
18767
18768         var p = Dom.getXY( el );
18769
18770         this.initPageX = p[0] - dx;
18771         this.initPageY = p[1] - dy;
18772
18773         this.lastPageX = p[0];
18774         this.lastPageY = p[1];
18775
18776
18777         this.setStartPosition(p);
18778     },
18779
18780     /**
18781      * Sets the start position of the element.  This is set when the obj
18782      * is initialized, the reset when a drag is started.
18783      * @method setStartPosition
18784      * @param pos current position (from previous lookup)
18785      * @private
18786      */
18787     setStartPosition: function(pos) {
18788         var p = pos || Dom.getXY( this.getEl() );
18789         this.deltaSetXY = null;
18790
18791         this.startPageX = p[0];
18792         this.startPageY = p[1];
18793     },
18794
18795     /**
18796      * Add this instance to a group of related drag/drop objects.  All
18797      * instances belong to at least one group, and can belong to as many
18798      * groups as needed.
18799      * @method addToGroup
18800      * @param sGroup {string} the name of the group
18801      */
18802     addToGroup: function(sGroup) {
18803         this.groups[sGroup] = true;
18804         this.DDM.regDragDrop(this, sGroup);
18805     },
18806
18807     /**
18808      * Remove's this instance from the supplied interaction group
18809      * @method removeFromGroup
18810      * @param {string}  sGroup  The group to drop
18811      */
18812     removeFromGroup: function(sGroup) {
18813         if (this.groups[sGroup]) {
18814             delete this.groups[sGroup];
18815         }
18816
18817         this.DDM.removeDDFromGroup(this, sGroup);
18818     },
18819
18820     /**
18821      * Allows you to specify that an element other than the linked element
18822      * will be moved with the cursor during a drag
18823      * @method setDragElId
18824      * @param id {string} the id of the element that will be used to initiate the drag
18825      */
18826     setDragElId: function(id) {
18827         this.dragElId = id;
18828     },
18829
18830     /**
18831      * Allows you to specify a child of the linked element that should be
18832      * used to initiate the drag operation.  An example of this would be if
18833      * you have a content div with text and links.  Clicking anywhere in the
18834      * content area would normally start the drag operation.  Use this method
18835      * to specify that an element inside of the content div is the element
18836      * that starts the drag operation.
18837      * @method setHandleElId
18838      * @param id {string} the id of the element that will be used to
18839      * initiate the drag.
18840      */
18841     setHandleElId: function(id) {
18842         if (typeof id !== "string") {
18843             id = Roo.id(id);
18844         }
18845         this.handleElId = id;
18846         this.DDM.regHandle(this.id, id);
18847     },
18848
18849     /**
18850      * Allows you to set an element outside of the linked element as a drag
18851      * handle
18852      * @method setOuterHandleElId
18853      * @param id the id of the element that will be used to initiate the drag
18854      */
18855     setOuterHandleElId: function(id) {
18856         if (typeof id !== "string") {
18857             id = Roo.id(id);
18858         }
18859         Event.on(id, "mousedown",
18860                 this.handleMouseDown, this);
18861         this.setHandleElId(id);
18862
18863         this.hasOuterHandles = true;
18864     },
18865
18866     /**
18867      * Remove all drag and drop hooks for this element
18868      * @method unreg
18869      */
18870     unreg: function() {
18871         Event.un(this.id, "mousedown",
18872                 this.handleMouseDown);
18873         Event.un(this.id, "touchstart",
18874                 this.handleMouseDown);
18875         this._domRef = null;
18876         this.DDM._remove(this);
18877     },
18878
18879     destroy : function(){
18880         this.unreg();
18881     },
18882
18883     /**
18884      * Returns true if this instance is locked, or the drag drop mgr is locked
18885      * (meaning that all drag/drop is disabled on the page.)
18886      * @method isLocked
18887      * @return {boolean} true if this obj or all drag/drop is locked, else
18888      * false
18889      */
18890     isLocked: function() {
18891         return (this.DDM.isLocked() || this.locked);
18892     },
18893
18894     /**
18895      * Fired when this object is clicked
18896      * @method handleMouseDown
18897      * @param {Event} e
18898      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18899      * @private
18900      */
18901     handleMouseDown: function(e, oDD){
18902      
18903         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18904             //Roo.log('not touch/ button !=0');
18905             return;
18906         }
18907         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18908             return; // double touch..
18909         }
18910         
18911
18912         if (this.isLocked()) {
18913             //Roo.log('locked');
18914             return;
18915         }
18916
18917         this.DDM.refreshCache(this.groups);
18918 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18919         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18920         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18921             //Roo.log('no outer handes or not over target');
18922                 // do nothing.
18923         } else {
18924 //            Roo.log('check validator');
18925             if (this.clickValidator(e)) {
18926 //                Roo.log('validate success');
18927                 // set the initial element position
18928                 this.setStartPosition();
18929
18930
18931                 this.b4MouseDown(e);
18932                 this.onMouseDown(e);
18933
18934                 this.DDM.handleMouseDown(e, this);
18935
18936                 this.DDM.stopEvent(e);
18937             } else {
18938
18939
18940             }
18941         }
18942     },
18943
18944     clickValidator: function(e) {
18945         var target = e.getTarget();
18946         return ( this.isValidHandleChild(target) &&
18947                     (this.id == this.handleElId ||
18948                         this.DDM.handleWasClicked(target, this.id)) );
18949     },
18950
18951     /**
18952      * Allows you to specify a tag name that should not start a drag operation
18953      * when clicked.  This is designed to facilitate embedding links within a
18954      * drag handle that do something other than start the drag.
18955      * @method addInvalidHandleType
18956      * @param {string} tagName the type of element to exclude
18957      */
18958     addInvalidHandleType: function(tagName) {
18959         var type = tagName.toUpperCase();
18960         this.invalidHandleTypes[type] = type;
18961     },
18962
18963     /**
18964      * Lets you to specify an element id for a child of a drag handle
18965      * that should not initiate a drag
18966      * @method addInvalidHandleId
18967      * @param {string} id the element id of the element you wish to ignore
18968      */
18969     addInvalidHandleId: function(id) {
18970         if (typeof id !== "string") {
18971             id = Roo.id(id);
18972         }
18973         this.invalidHandleIds[id] = id;
18974     },
18975
18976     /**
18977      * Lets you specify a css class of elements that will not initiate a drag
18978      * @method addInvalidHandleClass
18979      * @param {string} cssClass the class of the elements you wish to ignore
18980      */
18981     addInvalidHandleClass: function(cssClass) {
18982         this.invalidHandleClasses.push(cssClass);
18983     },
18984
18985     /**
18986      * Unsets an excluded tag name set by addInvalidHandleType
18987      * @method removeInvalidHandleType
18988      * @param {string} tagName the type of element to unexclude
18989      */
18990     removeInvalidHandleType: function(tagName) {
18991         var type = tagName.toUpperCase();
18992         // this.invalidHandleTypes[type] = null;
18993         delete this.invalidHandleTypes[type];
18994     },
18995
18996     /**
18997      * Unsets an invalid handle id
18998      * @method removeInvalidHandleId
18999      * @param {string} id the id of the element to re-enable
19000      */
19001     removeInvalidHandleId: function(id) {
19002         if (typeof id !== "string") {
19003             id = Roo.id(id);
19004         }
19005         delete this.invalidHandleIds[id];
19006     },
19007
19008     /**
19009      * Unsets an invalid css class
19010      * @method removeInvalidHandleClass
19011      * @param {string} cssClass the class of the element(s) you wish to
19012      * re-enable
19013      */
19014     removeInvalidHandleClass: function(cssClass) {
19015         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19016             if (this.invalidHandleClasses[i] == cssClass) {
19017                 delete this.invalidHandleClasses[i];
19018             }
19019         }
19020     },
19021
19022     /**
19023      * Checks the tag exclusion list to see if this click should be ignored
19024      * @method isValidHandleChild
19025      * @param {HTMLElement} node the HTMLElement to evaluate
19026      * @return {boolean} true if this is a valid tag type, false if not
19027      */
19028     isValidHandleChild: function(node) {
19029
19030         var valid = true;
19031         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19032         var nodeName;
19033         try {
19034             nodeName = node.nodeName.toUpperCase();
19035         } catch(e) {
19036             nodeName = node.nodeName;
19037         }
19038         valid = valid && !this.invalidHandleTypes[nodeName];
19039         valid = valid && !this.invalidHandleIds[node.id];
19040
19041         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19042             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19043         }
19044
19045
19046         return valid;
19047
19048     },
19049
19050     /**
19051      * Create the array of horizontal tick marks if an interval was specified
19052      * in setXConstraint().
19053      * @method setXTicks
19054      * @private
19055      */
19056     setXTicks: function(iStartX, iTickSize) {
19057         this.xTicks = [];
19058         this.xTickSize = iTickSize;
19059
19060         var tickMap = {};
19061
19062         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19063             if (!tickMap[i]) {
19064                 this.xTicks[this.xTicks.length] = i;
19065                 tickMap[i] = true;
19066             }
19067         }
19068
19069         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19070             if (!tickMap[i]) {
19071                 this.xTicks[this.xTicks.length] = i;
19072                 tickMap[i] = true;
19073             }
19074         }
19075
19076         this.xTicks.sort(this.DDM.numericSort) ;
19077     },
19078
19079     /**
19080      * Create the array of vertical tick marks if an interval was specified in
19081      * setYConstraint().
19082      * @method setYTicks
19083      * @private
19084      */
19085     setYTicks: function(iStartY, iTickSize) {
19086         this.yTicks = [];
19087         this.yTickSize = iTickSize;
19088
19089         var tickMap = {};
19090
19091         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19092             if (!tickMap[i]) {
19093                 this.yTicks[this.yTicks.length] = i;
19094                 tickMap[i] = true;
19095             }
19096         }
19097
19098         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19099             if (!tickMap[i]) {
19100                 this.yTicks[this.yTicks.length] = i;
19101                 tickMap[i] = true;
19102             }
19103         }
19104
19105         this.yTicks.sort(this.DDM.numericSort) ;
19106     },
19107
19108     /**
19109      * By default, the element can be dragged any place on the screen.  Use
19110      * this method to limit the horizontal travel of the element.  Pass in
19111      * 0,0 for the parameters if you want to lock the drag to the y axis.
19112      * @method setXConstraint
19113      * @param {int} iLeft the number of pixels the element can move to the left
19114      * @param {int} iRight the number of pixels the element can move to the
19115      * right
19116      * @param {int} iTickSize optional parameter for specifying that the
19117      * element
19118      * should move iTickSize pixels at a time.
19119      */
19120     setXConstraint: function(iLeft, iRight, iTickSize) {
19121         this.leftConstraint = iLeft;
19122         this.rightConstraint = iRight;
19123
19124         this.minX = this.initPageX - iLeft;
19125         this.maxX = this.initPageX + iRight;
19126         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19127
19128         this.constrainX = true;
19129     },
19130
19131     /**
19132      * Clears any constraints applied to this instance.  Also clears ticks
19133      * since they can't exist independent of a constraint at this time.
19134      * @method clearConstraints
19135      */
19136     clearConstraints: function() {
19137         this.constrainX = false;
19138         this.constrainY = false;
19139         this.clearTicks();
19140     },
19141
19142     /**
19143      * Clears any tick interval defined for this instance
19144      * @method clearTicks
19145      */
19146     clearTicks: function() {
19147         this.xTicks = null;
19148         this.yTicks = null;
19149         this.xTickSize = 0;
19150         this.yTickSize = 0;
19151     },
19152
19153     /**
19154      * By default, the element can be dragged any place on the screen.  Set
19155      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19156      * parameters if you want to lock the drag to the x axis.
19157      * @method setYConstraint
19158      * @param {int} iUp the number of pixels the element can move up
19159      * @param {int} iDown the number of pixels the element can move down
19160      * @param {int} iTickSize optional parameter for specifying that the
19161      * element should move iTickSize pixels at a time.
19162      */
19163     setYConstraint: function(iUp, iDown, iTickSize) {
19164         this.topConstraint = iUp;
19165         this.bottomConstraint = iDown;
19166
19167         this.minY = this.initPageY - iUp;
19168         this.maxY = this.initPageY + iDown;
19169         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19170
19171         this.constrainY = true;
19172
19173     },
19174
19175     /**
19176      * resetConstraints must be called if you manually reposition a dd element.
19177      * @method resetConstraints
19178      * @param {boolean} maintainOffset
19179      */
19180     resetConstraints: function() {
19181
19182
19183         // Maintain offsets if necessary
19184         if (this.initPageX || this.initPageX === 0) {
19185             // figure out how much this thing has moved
19186             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19187             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19188
19189             this.setInitPosition(dx, dy);
19190
19191         // This is the first time we have detected the element's position
19192         } else {
19193             this.setInitPosition();
19194         }
19195
19196         if (this.constrainX) {
19197             this.setXConstraint( this.leftConstraint,
19198                                  this.rightConstraint,
19199                                  this.xTickSize        );
19200         }
19201
19202         if (this.constrainY) {
19203             this.setYConstraint( this.topConstraint,
19204                                  this.bottomConstraint,
19205                                  this.yTickSize         );
19206         }
19207     },
19208
19209     /**
19210      * Normally the drag element is moved pixel by pixel, but we can specify
19211      * that it move a number of pixels at a time.  This method resolves the
19212      * location when we have it set up like this.
19213      * @method getTick
19214      * @param {int} val where we want to place the object
19215      * @param {int[]} tickArray sorted array of valid points
19216      * @return {int} the closest tick
19217      * @private
19218      */
19219     getTick: function(val, tickArray) {
19220
19221         if (!tickArray) {
19222             // If tick interval is not defined, it is effectively 1 pixel,
19223             // so we return the value passed to us.
19224             return val;
19225         } else if (tickArray[0] >= val) {
19226             // The value is lower than the first tick, so we return the first
19227             // tick.
19228             return tickArray[0];
19229         } else {
19230             for (var i=0, len=tickArray.length; i<len; ++i) {
19231                 var next = i + 1;
19232                 if (tickArray[next] && tickArray[next] >= val) {
19233                     var diff1 = val - tickArray[i];
19234                     var diff2 = tickArray[next] - val;
19235                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19236                 }
19237             }
19238
19239             // The value is larger than the last tick, so we return the last
19240             // tick.
19241             return tickArray[tickArray.length - 1];
19242         }
19243     },
19244
19245     /**
19246      * toString method
19247      * @method toString
19248      * @return {string} string representation of the dd obj
19249      */
19250     toString: function() {
19251         return ("DragDrop " + this.id);
19252     }
19253
19254 });
19255
19256 })();
19257 /*
19258  * Based on:
19259  * Ext JS Library 1.1.1
19260  * Copyright(c) 2006-2007, Ext JS, LLC.
19261  *
19262  * Originally Released Under LGPL - original licence link has changed is not relivant.
19263  *
19264  * Fork - LGPL
19265  * <script type="text/javascript">
19266  */
19267
19268
19269 /**
19270  * The drag and drop utility provides a framework for building drag and drop
19271  * applications.  In addition to enabling drag and drop for specific elements,
19272  * the drag and drop elements are tracked by the manager class, and the
19273  * interactions between the various elements are tracked during the drag and
19274  * the implementing code is notified about these important moments.
19275  */
19276
19277 // Only load the library once.  Rewriting the manager class would orphan
19278 // existing drag and drop instances.
19279 if (!Roo.dd.DragDropMgr) {
19280
19281 /**
19282  * @class Roo.dd.DragDropMgr
19283  * DragDropMgr is a singleton that tracks the element interaction for
19284  * all DragDrop items in the window.  Generally, you will not call
19285  * this class directly, but it does have helper methods that could
19286  * be useful in your DragDrop implementations.
19287  * @singleton
19288  */
19289 Roo.dd.DragDropMgr = function() {
19290
19291     var Event = Roo.EventManager;
19292
19293     return {
19294
19295         /**
19296          * Two dimensional Array of registered DragDrop objects.  The first
19297          * dimension is the DragDrop item group, the second the DragDrop
19298          * object.
19299          * @property ids
19300          * @type {string: string}
19301          * @private
19302          * @static
19303          */
19304         ids: {},
19305
19306         /**
19307          * Array of element ids defined as drag handles.  Used to determine
19308          * if the element that generated the mousedown event is actually the
19309          * handle and not the html element itself.
19310          * @property handleIds
19311          * @type {string: string}
19312          * @private
19313          * @static
19314          */
19315         handleIds: {},
19316
19317         /**
19318          * the DragDrop object that is currently being dragged
19319          * @property dragCurrent
19320          * @type DragDrop
19321          * @private
19322          * @static
19323          **/
19324         dragCurrent: null,
19325
19326         /**
19327          * the DragDrop object(s) that are being hovered over
19328          * @property dragOvers
19329          * @type Array
19330          * @private
19331          * @static
19332          */
19333         dragOvers: {},
19334
19335         /**
19336          * the X distance between the cursor and the object being dragged
19337          * @property deltaX
19338          * @type int
19339          * @private
19340          * @static
19341          */
19342         deltaX: 0,
19343
19344         /**
19345          * the Y distance between the cursor and the object being dragged
19346          * @property deltaY
19347          * @type int
19348          * @private
19349          * @static
19350          */
19351         deltaY: 0,
19352
19353         /**
19354          * Flag to determine if we should prevent the default behavior of the
19355          * events we define. By default this is true, but this can be set to
19356          * false if you need the default behavior (not recommended)
19357          * @property preventDefault
19358          * @type boolean
19359          * @static
19360          */
19361         preventDefault: true,
19362
19363         /**
19364          * Flag to determine if we should stop the propagation of the events
19365          * we generate. This is true by default but you may want to set it to
19366          * false if the html element contains other features that require the
19367          * mouse click.
19368          * @property stopPropagation
19369          * @type boolean
19370          * @static
19371          */
19372         stopPropagation: true,
19373
19374         /**
19375          * Internal flag that is set to true when drag and drop has been
19376          * intialized
19377          * @property initialized
19378          * @private
19379          * @static
19380          */
19381         initalized: false,
19382
19383         /**
19384          * All drag and drop can be disabled.
19385          * @property locked
19386          * @private
19387          * @static
19388          */
19389         locked: false,
19390
19391         /**
19392          * Called the first time an element is registered.
19393          * @method init
19394          * @private
19395          * @static
19396          */
19397         init: function() {
19398             this.initialized = true;
19399         },
19400
19401         /**
19402          * In point mode, drag and drop interaction is defined by the
19403          * location of the cursor during the drag/drop
19404          * @property POINT
19405          * @type int
19406          * @static
19407          */
19408         POINT: 0,
19409
19410         /**
19411          * In intersect mode, drag and drop interactio nis defined by the
19412          * overlap of two or more drag and drop objects.
19413          * @property INTERSECT
19414          * @type int
19415          * @static
19416          */
19417         INTERSECT: 1,
19418
19419         /**
19420          * The current drag and drop mode.  Default: POINT
19421          * @property mode
19422          * @type int
19423          * @static
19424          */
19425         mode: 0,
19426
19427         /**
19428          * Runs method on all drag and drop objects
19429          * @method _execOnAll
19430          * @private
19431          * @static
19432          */
19433         _execOnAll: function(sMethod, args) {
19434             for (var i in this.ids) {
19435                 for (var j in this.ids[i]) {
19436                     var oDD = this.ids[i][j];
19437                     if (! this.isTypeOfDD(oDD)) {
19438                         continue;
19439                     }
19440                     oDD[sMethod].apply(oDD, args);
19441                 }
19442             }
19443         },
19444
19445         /**
19446          * Drag and drop initialization.  Sets up the global event handlers
19447          * @method _onLoad
19448          * @private
19449          * @static
19450          */
19451         _onLoad: function() {
19452
19453             this.init();
19454
19455             if (!Roo.isTouch) {
19456                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19457                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19458             }
19459             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19460             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19461             
19462             Event.on(window,   "unload",    this._onUnload, this, true);
19463             Event.on(window,   "resize",    this._onResize, this, true);
19464             // Event.on(window,   "mouseout",    this._test);
19465
19466         },
19467
19468         /**
19469          * Reset constraints on all drag and drop objs
19470          * @method _onResize
19471          * @private
19472          * @static
19473          */
19474         _onResize: function(e) {
19475             this._execOnAll("resetConstraints", []);
19476         },
19477
19478         /**
19479          * Lock all drag and drop functionality
19480          * @method lock
19481          * @static
19482          */
19483         lock: function() { this.locked = true; },
19484
19485         /**
19486          * Unlock all drag and drop functionality
19487          * @method unlock
19488          * @static
19489          */
19490         unlock: function() { this.locked = false; },
19491
19492         /**
19493          * Is drag and drop locked?
19494          * @method isLocked
19495          * @return {boolean} True if drag and drop is locked, false otherwise.
19496          * @static
19497          */
19498         isLocked: function() { return this.locked; },
19499
19500         /**
19501          * Location cache that is set for all drag drop objects when a drag is
19502          * initiated, cleared when the drag is finished.
19503          * @property locationCache
19504          * @private
19505          * @static
19506          */
19507         locationCache: {},
19508
19509         /**
19510          * Set useCache to false if you want to force object the lookup of each
19511          * drag and drop linked element constantly during a drag.
19512          * @property useCache
19513          * @type boolean
19514          * @static
19515          */
19516         useCache: true,
19517
19518         /**
19519          * The number of pixels that the mouse needs to move after the
19520          * mousedown before the drag is initiated.  Default=3;
19521          * @property clickPixelThresh
19522          * @type int
19523          * @static
19524          */
19525         clickPixelThresh: 3,
19526
19527         /**
19528          * The number of milliseconds after the mousedown event to initiate the
19529          * drag if we don't get a mouseup event. Default=1000
19530          * @property clickTimeThresh
19531          * @type int
19532          * @static
19533          */
19534         clickTimeThresh: 350,
19535
19536         /**
19537          * Flag that indicates that either the drag pixel threshold or the
19538          * mousdown time threshold has been met
19539          * @property dragThreshMet
19540          * @type boolean
19541          * @private
19542          * @static
19543          */
19544         dragThreshMet: false,
19545
19546         /**
19547          * Timeout used for the click time threshold
19548          * @property clickTimeout
19549          * @type Object
19550          * @private
19551          * @static
19552          */
19553         clickTimeout: null,
19554
19555         /**
19556          * The X position of the mousedown event stored for later use when a
19557          * drag threshold is met.
19558          * @property startX
19559          * @type int
19560          * @private
19561          * @static
19562          */
19563         startX: 0,
19564
19565         /**
19566          * The Y position of the mousedown event stored for later use when a
19567          * drag threshold is met.
19568          * @property startY
19569          * @type int
19570          * @private
19571          * @static
19572          */
19573         startY: 0,
19574
19575         /**
19576          * Each DragDrop instance must be registered with the DragDropMgr.
19577          * This is executed in DragDrop.init()
19578          * @method regDragDrop
19579          * @param {DragDrop} oDD the DragDrop object to register
19580          * @param {String} sGroup the name of the group this element belongs to
19581          * @static
19582          */
19583         regDragDrop: function(oDD, sGroup) {
19584             if (!this.initialized) { this.init(); }
19585
19586             if (!this.ids[sGroup]) {
19587                 this.ids[sGroup] = {};
19588             }
19589             this.ids[sGroup][oDD.id] = oDD;
19590         },
19591
19592         /**
19593          * Removes the supplied dd instance from the supplied group. Executed
19594          * by DragDrop.removeFromGroup, so don't call this function directly.
19595          * @method removeDDFromGroup
19596          * @private
19597          * @static
19598          */
19599         removeDDFromGroup: function(oDD, sGroup) {
19600             if (!this.ids[sGroup]) {
19601                 this.ids[sGroup] = {};
19602             }
19603
19604             var obj = this.ids[sGroup];
19605             if (obj && obj[oDD.id]) {
19606                 delete obj[oDD.id];
19607             }
19608         },
19609
19610         /**
19611          * Unregisters a drag and drop item.  This is executed in
19612          * DragDrop.unreg, use that method instead of calling this directly.
19613          * @method _remove
19614          * @private
19615          * @static
19616          */
19617         _remove: function(oDD) {
19618             for (var g in oDD.groups) {
19619                 if (g && this.ids[g][oDD.id]) {
19620                     delete this.ids[g][oDD.id];
19621                 }
19622             }
19623             delete this.handleIds[oDD.id];
19624         },
19625
19626         /**
19627          * Each DragDrop handle element must be registered.  This is done
19628          * automatically when executing DragDrop.setHandleElId()
19629          * @method regHandle
19630          * @param {String} sDDId the DragDrop id this element is a handle for
19631          * @param {String} sHandleId the id of the element that is the drag
19632          * handle
19633          * @static
19634          */
19635         regHandle: function(sDDId, sHandleId) {
19636             if (!this.handleIds[sDDId]) {
19637                 this.handleIds[sDDId] = {};
19638             }
19639             this.handleIds[sDDId][sHandleId] = sHandleId;
19640         },
19641
19642         /**
19643          * Utility function to determine if a given element has been
19644          * registered as a drag drop item.
19645          * @method isDragDrop
19646          * @param {String} id the element id to check
19647          * @return {boolean} true if this element is a DragDrop item,
19648          * false otherwise
19649          * @static
19650          */
19651         isDragDrop: function(id) {
19652             return ( this.getDDById(id) ) ? true : false;
19653         },
19654
19655         /**
19656          * Returns the drag and drop instances that are in all groups the
19657          * passed in instance belongs to.
19658          * @method getRelated
19659          * @param {DragDrop} p_oDD the obj to get related data for
19660          * @param {boolean} bTargetsOnly if true, only return targetable objs
19661          * @return {DragDrop[]} the related instances
19662          * @static
19663          */
19664         getRelated: function(p_oDD, bTargetsOnly) {
19665             var oDDs = [];
19666             for (var i in p_oDD.groups) {
19667                 for (j in this.ids[i]) {
19668                     var dd = this.ids[i][j];
19669                     if (! this.isTypeOfDD(dd)) {
19670                         continue;
19671                     }
19672                     if (!bTargetsOnly || dd.isTarget) {
19673                         oDDs[oDDs.length] = dd;
19674                     }
19675                 }
19676             }
19677
19678             return oDDs;
19679         },
19680
19681         /**
19682          * Returns true if the specified dd target is a legal target for
19683          * the specifice drag obj
19684          * @method isLegalTarget
19685          * @param {DragDrop} the drag obj
19686          * @param {DragDrop} the target
19687          * @return {boolean} true if the target is a legal target for the
19688          * dd obj
19689          * @static
19690          */
19691         isLegalTarget: function (oDD, oTargetDD) {
19692             var targets = this.getRelated(oDD, true);
19693             for (var i=0, len=targets.length;i<len;++i) {
19694                 if (targets[i].id == oTargetDD.id) {
19695                     return true;
19696                 }
19697             }
19698
19699             return false;
19700         },
19701
19702         /**
19703          * My goal is to be able to transparently determine if an object is
19704          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19705          * returns "object", oDD.constructor.toString() always returns
19706          * "DragDrop" and not the name of the subclass.  So for now it just
19707          * evaluates a well-known variable in DragDrop.
19708          * @method isTypeOfDD
19709          * @param {Object} the object to evaluate
19710          * @return {boolean} true if typeof oDD = DragDrop
19711          * @static
19712          */
19713         isTypeOfDD: function (oDD) {
19714             return (oDD && oDD.__ygDragDrop);
19715         },
19716
19717         /**
19718          * Utility function to determine if a given element has been
19719          * registered as a drag drop handle for the given Drag Drop object.
19720          * @method isHandle
19721          * @param {String} id the element id to check
19722          * @return {boolean} true if this element is a DragDrop handle, false
19723          * otherwise
19724          * @static
19725          */
19726         isHandle: function(sDDId, sHandleId) {
19727             return ( this.handleIds[sDDId] &&
19728                             this.handleIds[sDDId][sHandleId] );
19729         },
19730
19731         /**
19732          * Returns the DragDrop instance for a given id
19733          * @method getDDById
19734          * @param {String} id the id of the DragDrop object
19735          * @return {DragDrop} the drag drop object, null if it is not found
19736          * @static
19737          */
19738         getDDById: function(id) {
19739             for (var i in this.ids) {
19740                 if (this.ids[i][id]) {
19741                     return this.ids[i][id];
19742                 }
19743             }
19744             return null;
19745         },
19746
19747         /**
19748          * Fired after a registered DragDrop object gets the mousedown event.
19749          * Sets up the events required to track the object being dragged
19750          * @method handleMouseDown
19751          * @param {Event} e the event
19752          * @param oDD the DragDrop object being dragged
19753          * @private
19754          * @static
19755          */
19756         handleMouseDown: function(e, oDD) {
19757             if(Roo.QuickTips){
19758                 Roo.QuickTips.disable();
19759             }
19760             this.currentTarget = e.getTarget();
19761
19762             this.dragCurrent = oDD;
19763
19764             var el = oDD.getEl();
19765
19766             // track start position
19767             this.startX = e.getPageX();
19768             this.startY = e.getPageY();
19769
19770             this.deltaX = this.startX - el.offsetLeft;
19771             this.deltaY = this.startY - el.offsetTop;
19772
19773             this.dragThreshMet = false;
19774
19775             this.clickTimeout = setTimeout(
19776                     function() {
19777                         var DDM = Roo.dd.DDM;
19778                         DDM.startDrag(DDM.startX, DDM.startY);
19779                     },
19780                     this.clickTimeThresh );
19781         },
19782
19783         /**
19784          * Fired when either the drag pixel threshol or the mousedown hold
19785          * time threshold has been met.
19786          * @method startDrag
19787          * @param x {int} the X position of the original mousedown
19788          * @param y {int} the Y position of the original mousedown
19789          * @static
19790          */
19791         startDrag: function(x, y) {
19792             clearTimeout(this.clickTimeout);
19793             if (this.dragCurrent) {
19794                 this.dragCurrent.b4StartDrag(x, y);
19795                 this.dragCurrent.startDrag(x, y);
19796             }
19797             this.dragThreshMet = true;
19798         },
19799
19800         /**
19801          * Internal function to handle the mouseup event.  Will be invoked
19802          * from the context of the document.
19803          * @method handleMouseUp
19804          * @param {Event} e the event
19805          * @private
19806          * @static
19807          */
19808         handleMouseUp: function(e) {
19809
19810             if(Roo.QuickTips){
19811                 Roo.QuickTips.enable();
19812             }
19813             if (! this.dragCurrent) {
19814                 return;
19815             }
19816
19817             clearTimeout(this.clickTimeout);
19818
19819             if (this.dragThreshMet) {
19820                 this.fireEvents(e, true);
19821             } else {
19822             }
19823
19824             this.stopDrag(e);
19825
19826             this.stopEvent(e);
19827         },
19828
19829         /**
19830          * Utility to stop event propagation and event default, if these
19831          * features are turned on.
19832          * @method stopEvent
19833          * @param {Event} e the event as returned by this.getEvent()
19834          * @static
19835          */
19836         stopEvent: function(e){
19837             if(this.stopPropagation) {
19838                 e.stopPropagation();
19839             }
19840
19841             if (this.preventDefault) {
19842                 e.preventDefault();
19843             }
19844         },
19845
19846         /**
19847          * Internal function to clean up event handlers after the drag
19848          * operation is complete
19849          * @method stopDrag
19850          * @param {Event} e the event
19851          * @private
19852          * @static
19853          */
19854         stopDrag: function(e) {
19855             // Fire the drag end event for the item that was dragged
19856             if (this.dragCurrent) {
19857                 if (this.dragThreshMet) {
19858                     this.dragCurrent.b4EndDrag(e);
19859                     this.dragCurrent.endDrag(e);
19860                 }
19861
19862                 this.dragCurrent.onMouseUp(e);
19863             }
19864
19865             this.dragCurrent = null;
19866             this.dragOvers = {};
19867         },
19868
19869         /**
19870          * Internal function to handle the mousemove event.  Will be invoked
19871          * from the context of the html element.
19872          *
19873          * @TODO figure out what we can do about mouse events lost when the
19874          * user drags objects beyond the window boundary.  Currently we can
19875          * detect this in internet explorer by verifying that the mouse is
19876          * down during the mousemove event.  Firefox doesn't give us the
19877          * button state on the mousemove event.
19878          * @method handleMouseMove
19879          * @param {Event} e the event
19880          * @private
19881          * @static
19882          */
19883         handleMouseMove: function(e) {
19884             if (! this.dragCurrent) {
19885                 return true;
19886             }
19887
19888             // var button = e.which || e.button;
19889
19890             // check for IE mouseup outside of page boundary
19891             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19892                 this.stopEvent(e);
19893                 return this.handleMouseUp(e);
19894             }
19895
19896             if (!this.dragThreshMet) {
19897                 var diffX = Math.abs(this.startX - e.getPageX());
19898                 var diffY = Math.abs(this.startY - e.getPageY());
19899                 if (diffX > this.clickPixelThresh ||
19900                             diffY > this.clickPixelThresh) {
19901                     this.startDrag(this.startX, this.startY);
19902                 }
19903             }
19904
19905             if (this.dragThreshMet) {
19906                 this.dragCurrent.b4Drag(e);
19907                 this.dragCurrent.onDrag(e);
19908                 if(!this.dragCurrent.moveOnly){
19909                     this.fireEvents(e, false);
19910                 }
19911             }
19912
19913             this.stopEvent(e);
19914
19915             return true;
19916         },
19917
19918         /**
19919          * Iterates over all of the DragDrop elements to find ones we are
19920          * hovering over or dropping on
19921          * @method fireEvents
19922          * @param {Event} e the event
19923          * @param {boolean} isDrop is this a drop op or a mouseover op?
19924          * @private
19925          * @static
19926          */
19927         fireEvents: function(e, isDrop) {
19928             var dc = this.dragCurrent;
19929
19930             // If the user did the mouse up outside of the window, we could
19931             // get here even though we have ended the drag.
19932             if (!dc || dc.isLocked()) {
19933                 return;
19934             }
19935
19936             var pt = e.getPoint();
19937
19938             // cache the previous dragOver array
19939             var oldOvers = [];
19940
19941             var outEvts   = [];
19942             var overEvts  = [];
19943             var dropEvts  = [];
19944             var enterEvts = [];
19945
19946             // Check to see if the object(s) we were hovering over is no longer
19947             // being hovered over so we can fire the onDragOut event
19948             for (var i in this.dragOvers) {
19949
19950                 var ddo = this.dragOvers[i];
19951
19952                 if (! this.isTypeOfDD(ddo)) {
19953                     continue;
19954                 }
19955
19956                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19957                     outEvts.push( ddo );
19958                 }
19959
19960                 oldOvers[i] = true;
19961                 delete this.dragOvers[i];
19962             }
19963
19964             for (var sGroup in dc.groups) {
19965
19966                 if ("string" != typeof sGroup) {
19967                     continue;
19968                 }
19969
19970                 for (i in this.ids[sGroup]) {
19971                     var oDD = this.ids[sGroup][i];
19972                     if (! this.isTypeOfDD(oDD)) {
19973                         continue;
19974                     }
19975
19976                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19977                         if (this.isOverTarget(pt, oDD, this.mode)) {
19978                             // look for drop interactions
19979                             if (isDrop) {
19980                                 dropEvts.push( oDD );
19981                             // look for drag enter and drag over interactions
19982                             } else {
19983
19984                                 // initial drag over: dragEnter fires
19985                                 if (!oldOvers[oDD.id]) {
19986                                     enterEvts.push( oDD );
19987                                 // subsequent drag overs: dragOver fires
19988                                 } else {
19989                                     overEvts.push( oDD );
19990                                 }
19991
19992                                 this.dragOvers[oDD.id] = oDD;
19993                             }
19994                         }
19995                     }
19996                 }
19997             }
19998
19999             if (this.mode) {
20000                 if (outEvts.length) {
20001                     dc.b4DragOut(e, outEvts);
20002                     dc.onDragOut(e, outEvts);
20003                 }
20004
20005                 if (enterEvts.length) {
20006                     dc.onDragEnter(e, enterEvts);
20007                 }
20008
20009                 if (overEvts.length) {
20010                     dc.b4DragOver(e, overEvts);
20011                     dc.onDragOver(e, overEvts);
20012                 }
20013
20014                 if (dropEvts.length) {
20015                     dc.b4DragDrop(e, dropEvts);
20016                     dc.onDragDrop(e, dropEvts);
20017                 }
20018
20019             } else {
20020                 // fire dragout events
20021                 var len = 0;
20022                 for (i=0, len=outEvts.length; i<len; ++i) {
20023                     dc.b4DragOut(e, outEvts[i].id);
20024                     dc.onDragOut(e, outEvts[i].id);
20025                 }
20026
20027                 // fire enter events
20028                 for (i=0,len=enterEvts.length; i<len; ++i) {
20029                     // dc.b4DragEnter(e, oDD.id);
20030                     dc.onDragEnter(e, enterEvts[i].id);
20031                 }
20032
20033                 // fire over events
20034                 for (i=0,len=overEvts.length; i<len; ++i) {
20035                     dc.b4DragOver(e, overEvts[i].id);
20036                     dc.onDragOver(e, overEvts[i].id);
20037                 }
20038
20039                 // fire drop events
20040                 for (i=0, len=dropEvts.length; i<len; ++i) {
20041                     dc.b4DragDrop(e, dropEvts[i].id);
20042                     dc.onDragDrop(e, dropEvts[i].id);
20043                 }
20044
20045             }
20046
20047             // notify about a drop that did not find a target
20048             if (isDrop && !dropEvts.length) {
20049                 dc.onInvalidDrop(e);
20050             }
20051
20052         },
20053
20054         /**
20055          * Helper function for getting the best match from the list of drag
20056          * and drop objects returned by the drag and drop events when we are
20057          * in INTERSECT mode.  It returns either the first object that the
20058          * cursor is over, or the object that has the greatest overlap with
20059          * the dragged element.
20060          * @method getBestMatch
20061          * @param  {DragDrop[]} dds The array of drag and drop objects
20062          * targeted
20063          * @return {DragDrop}       The best single match
20064          * @static
20065          */
20066         getBestMatch: function(dds) {
20067             var winner = null;
20068             // Return null if the input is not what we expect
20069             //if (!dds || !dds.length || dds.length == 0) {
20070                // winner = null;
20071             // If there is only one item, it wins
20072             //} else if (dds.length == 1) {
20073
20074             var len = dds.length;
20075
20076             if (len == 1) {
20077                 winner = dds[0];
20078             } else {
20079                 // Loop through the targeted items
20080                 for (var i=0; i<len; ++i) {
20081                     var dd = dds[i];
20082                     // If the cursor is over the object, it wins.  If the
20083                     // cursor is over multiple matches, the first one we come
20084                     // to wins.
20085                     if (dd.cursorIsOver) {
20086                         winner = dd;
20087                         break;
20088                     // Otherwise the object with the most overlap wins
20089                     } else {
20090                         if (!winner ||
20091                             winner.overlap.getArea() < dd.overlap.getArea()) {
20092                             winner = dd;
20093                         }
20094                     }
20095                 }
20096             }
20097
20098             return winner;
20099         },
20100
20101         /**
20102          * Refreshes the cache of the top-left and bottom-right points of the
20103          * drag and drop objects in the specified group(s).  This is in the
20104          * format that is stored in the drag and drop instance, so typical
20105          * usage is:
20106          * <code>
20107          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20108          * </code>
20109          * Alternatively:
20110          * <code>
20111          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20112          * </code>
20113          * @TODO this really should be an indexed array.  Alternatively this
20114          * method could accept both.
20115          * @method refreshCache
20116          * @param {Object} groups an associative array of groups to refresh
20117          * @static
20118          */
20119         refreshCache: function(groups) {
20120             for (var sGroup in groups) {
20121                 if ("string" != typeof sGroup) {
20122                     continue;
20123                 }
20124                 for (var i in this.ids[sGroup]) {
20125                     var oDD = this.ids[sGroup][i];
20126
20127                     if (this.isTypeOfDD(oDD)) {
20128                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20129                         var loc = this.getLocation(oDD);
20130                         if (loc) {
20131                             this.locationCache[oDD.id] = loc;
20132                         } else {
20133                             delete this.locationCache[oDD.id];
20134                             // this will unregister the drag and drop object if
20135                             // the element is not in a usable state
20136                             // oDD.unreg();
20137                         }
20138                     }
20139                 }
20140             }
20141         },
20142
20143         /**
20144          * This checks to make sure an element exists and is in the DOM.  The
20145          * main purpose is to handle cases where innerHTML is used to remove
20146          * drag and drop objects from the DOM.  IE provides an 'unspecified
20147          * error' when trying to access the offsetParent of such an element
20148          * @method verifyEl
20149          * @param {HTMLElement} el the element to check
20150          * @return {boolean} true if the element looks usable
20151          * @static
20152          */
20153         verifyEl: function(el) {
20154             if (el) {
20155                 var parent;
20156                 if(Roo.isIE){
20157                     try{
20158                         parent = el.offsetParent;
20159                     }catch(e){}
20160                 }else{
20161                     parent = el.offsetParent;
20162                 }
20163                 if (parent) {
20164                     return true;
20165                 }
20166             }
20167
20168             return false;
20169         },
20170
20171         /**
20172          * Returns a Region object containing the drag and drop element's position
20173          * and size, including the padding configured for it
20174          * @method getLocation
20175          * @param {DragDrop} oDD the drag and drop object to get the
20176          *                       location for
20177          * @return {Roo.lib.Region} a Region object representing the total area
20178          *                             the element occupies, including any padding
20179          *                             the instance is configured for.
20180          * @static
20181          */
20182         getLocation: function(oDD) {
20183             if (! this.isTypeOfDD(oDD)) {
20184                 return null;
20185             }
20186
20187             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20188
20189             try {
20190                 pos= Roo.lib.Dom.getXY(el);
20191             } catch (e) { }
20192
20193             if (!pos) {
20194                 return null;
20195             }
20196
20197             x1 = pos[0];
20198             x2 = x1 + el.offsetWidth;
20199             y1 = pos[1];
20200             y2 = y1 + el.offsetHeight;
20201
20202             t = y1 - oDD.padding[0];
20203             r = x2 + oDD.padding[1];
20204             b = y2 + oDD.padding[2];
20205             l = x1 - oDD.padding[3];
20206
20207             return new Roo.lib.Region( t, r, b, l );
20208         },
20209
20210         /**
20211          * Checks the cursor location to see if it over the target
20212          * @method isOverTarget
20213          * @param {Roo.lib.Point} pt The point to evaluate
20214          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20215          * @return {boolean} true if the mouse is over the target
20216          * @private
20217          * @static
20218          */
20219         isOverTarget: function(pt, oTarget, intersect) {
20220             // use cache if available
20221             var loc = this.locationCache[oTarget.id];
20222             if (!loc || !this.useCache) {
20223                 loc = this.getLocation(oTarget);
20224                 this.locationCache[oTarget.id] = loc;
20225
20226             }
20227
20228             if (!loc) {
20229                 return false;
20230             }
20231
20232             oTarget.cursorIsOver = loc.contains( pt );
20233
20234             // DragDrop is using this as a sanity check for the initial mousedown
20235             // in this case we are done.  In POINT mode, if the drag obj has no
20236             // contraints, we are also done. Otherwise we need to evaluate the
20237             // location of the target as related to the actual location of the
20238             // dragged element.
20239             var dc = this.dragCurrent;
20240             if (!dc || !dc.getTargetCoord ||
20241                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20242                 return oTarget.cursorIsOver;
20243             }
20244
20245             oTarget.overlap = null;
20246
20247             // Get the current location of the drag element, this is the
20248             // location of the mouse event less the delta that represents
20249             // where the original mousedown happened on the element.  We
20250             // need to consider constraints and ticks as well.
20251             var pos = dc.getTargetCoord(pt.x, pt.y);
20252
20253             var el = dc.getDragEl();
20254             var curRegion = new Roo.lib.Region( pos.y,
20255                                                    pos.x + el.offsetWidth,
20256                                                    pos.y + el.offsetHeight,
20257                                                    pos.x );
20258
20259             var overlap = curRegion.intersect(loc);
20260
20261             if (overlap) {
20262                 oTarget.overlap = overlap;
20263                 return (intersect) ? true : oTarget.cursorIsOver;
20264             } else {
20265                 return false;
20266             }
20267         },
20268
20269         /**
20270          * unload event handler
20271          * @method _onUnload
20272          * @private
20273          * @static
20274          */
20275         _onUnload: function(e, me) {
20276             Roo.dd.DragDropMgr.unregAll();
20277         },
20278
20279         /**
20280          * Cleans up the drag and drop events and objects.
20281          * @method unregAll
20282          * @private
20283          * @static
20284          */
20285         unregAll: function() {
20286
20287             if (this.dragCurrent) {
20288                 this.stopDrag();
20289                 this.dragCurrent = null;
20290             }
20291
20292             this._execOnAll("unreg", []);
20293
20294             for (i in this.elementCache) {
20295                 delete this.elementCache[i];
20296             }
20297
20298             this.elementCache = {};
20299             this.ids = {};
20300         },
20301
20302         /**
20303          * A cache of DOM elements
20304          * @property elementCache
20305          * @private
20306          * @static
20307          */
20308         elementCache: {},
20309
20310         /**
20311          * Get the wrapper for the DOM element specified
20312          * @method getElWrapper
20313          * @param {String} id the id of the element to get
20314          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20315          * @private
20316          * @deprecated This wrapper isn't that useful
20317          * @static
20318          */
20319         getElWrapper: function(id) {
20320             var oWrapper = this.elementCache[id];
20321             if (!oWrapper || !oWrapper.el) {
20322                 oWrapper = this.elementCache[id] =
20323                     new this.ElementWrapper(Roo.getDom(id));
20324             }
20325             return oWrapper;
20326         },
20327
20328         /**
20329          * Returns the actual DOM element
20330          * @method getElement
20331          * @param {String} id the id of the elment to get
20332          * @return {Object} The element
20333          * @deprecated use Roo.getDom instead
20334          * @static
20335          */
20336         getElement: function(id) {
20337             return Roo.getDom(id);
20338         },
20339
20340         /**
20341          * Returns the style property for the DOM element (i.e.,
20342          * document.getElById(id).style)
20343          * @method getCss
20344          * @param {String} id the id of the elment to get
20345          * @return {Object} The style property of the element
20346          * @deprecated use Roo.getDom instead
20347          * @static
20348          */
20349         getCss: function(id) {
20350             var el = Roo.getDom(id);
20351             return (el) ? el.style : null;
20352         },
20353
20354         /**
20355          * Inner class for cached elements
20356          * @class DragDropMgr.ElementWrapper
20357          * @for DragDropMgr
20358          * @private
20359          * @deprecated
20360          */
20361         ElementWrapper: function(el) {
20362                 /**
20363                  * The element
20364                  * @property el
20365                  */
20366                 this.el = el || null;
20367                 /**
20368                  * The element id
20369                  * @property id
20370                  */
20371                 this.id = this.el && el.id;
20372                 /**
20373                  * A reference to the style property
20374                  * @property css
20375                  */
20376                 this.css = this.el && el.style;
20377             },
20378
20379         /**
20380          * Returns the X position of an html element
20381          * @method getPosX
20382          * @param el the element for which to get the position
20383          * @return {int} the X coordinate
20384          * @for DragDropMgr
20385          * @deprecated use Roo.lib.Dom.getX instead
20386          * @static
20387          */
20388         getPosX: function(el) {
20389             return Roo.lib.Dom.getX(el);
20390         },
20391
20392         /**
20393          * Returns the Y position of an html element
20394          * @method getPosY
20395          * @param el the element for which to get the position
20396          * @return {int} the Y coordinate
20397          * @deprecated use Roo.lib.Dom.getY instead
20398          * @static
20399          */
20400         getPosY: function(el) {
20401             return Roo.lib.Dom.getY(el);
20402         },
20403
20404         /**
20405          * Swap two nodes.  In IE, we use the native method, for others we
20406          * emulate the IE behavior
20407          * @method swapNode
20408          * @param n1 the first node to swap
20409          * @param n2 the other node to swap
20410          * @static
20411          */
20412         swapNode: function(n1, n2) {
20413             if (n1.swapNode) {
20414                 n1.swapNode(n2);
20415             } else {
20416                 var p = n2.parentNode;
20417                 var s = n2.nextSibling;
20418
20419                 if (s == n1) {
20420                     p.insertBefore(n1, n2);
20421                 } else if (n2 == n1.nextSibling) {
20422                     p.insertBefore(n2, n1);
20423                 } else {
20424                     n1.parentNode.replaceChild(n2, n1);
20425                     p.insertBefore(n1, s);
20426                 }
20427             }
20428         },
20429
20430         /**
20431          * Returns the current scroll position
20432          * @method getScroll
20433          * @private
20434          * @static
20435          */
20436         getScroll: function () {
20437             var t, l, dde=document.documentElement, db=document.body;
20438             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20439                 t = dde.scrollTop;
20440                 l = dde.scrollLeft;
20441             } else if (db) {
20442                 t = db.scrollTop;
20443                 l = db.scrollLeft;
20444             } else {
20445
20446             }
20447             return { top: t, left: l };
20448         },
20449
20450         /**
20451          * Returns the specified element style property
20452          * @method getStyle
20453          * @param {HTMLElement} el          the element
20454          * @param {string}      styleProp   the style property
20455          * @return {string} The value of the style property
20456          * @deprecated use Roo.lib.Dom.getStyle
20457          * @static
20458          */
20459         getStyle: function(el, styleProp) {
20460             return Roo.fly(el).getStyle(styleProp);
20461         },
20462
20463         /**
20464          * Gets the scrollTop
20465          * @method getScrollTop
20466          * @return {int} the document's scrollTop
20467          * @static
20468          */
20469         getScrollTop: function () { return this.getScroll().top; },
20470
20471         /**
20472          * Gets the scrollLeft
20473          * @method getScrollLeft
20474          * @return {int} the document's scrollTop
20475          * @static
20476          */
20477         getScrollLeft: function () { return this.getScroll().left; },
20478
20479         /**
20480          * Sets the x/y position of an element to the location of the
20481          * target element.
20482          * @method moveToEl
20483          * @param {HTMLElement} moveEl      The element to move
20484          * @param {HTMLElement} targetEl    The position reference element
20485          * @static
20486          */
20487         moveToEl: function (moveEl, targetEl) {
20488             var aCoord = Roo.lib.Dom.getXY(targetEl);
20489             Roo.lib.Dom.setXY(moveEl, aCoord);
20490         },
20491
20492         /**
20493          * Numeric array sort function
20494          * @method numericSort
20495          * @static
20496          */
20497         numericSort: function(a, b) { return (a - b); },
20498
20499         /**
20500          * Internal counter
20501          * @property _timeoutCount
20502          * @private
20503          * @static
20504          */
20505         _timeoutCount: 0,
20506
20507         /**
20508          * Trying to make the load order less important.  Without this we get
20509          * an error if this file is loaded before the Event Utility.
20510          * @method _addListeners
20511          * @private
20512          * @static
20513          */
20514         _addListeners: function() {
20515             var DDM = Roo.dd.DDM;
20516             if ( Roo.lib.Event && document ) {
20517                 DDM._onLoad();
20518             } else {
20519                 if (DDM._timeoutCount > 2000) {
20520                 } else {
20521                     setTimeout(DDM._addListeners, 10);
20522                     if (document && document.body) {
20523                         DDM._timeoutCount += 1;
20524                     }
20525                 }
20526             }
20527         },
20528
20529         /**
20530          * Recursively searches the immediate parent and all child nodes for
20531          * the handle element in order to determine wheter or not it was
20532          * clicked.
20533          * @method handleWasClicked
20534          * @param node the html element to inspect
20535          * @static
20536          */
20537         handleWasClicked: function(node, id) {
20538             if (this.isHandle(id, node.id)) {
20539                 return true;
20540             } else {
20541                 // check to see if this is a text node child of the one we want
20542                 var p = node.parentNode;
20543
20544                 while (p) {
20545                     if (this.isHandle(id, p.id)) {
20546                         return true;
20547                     } else {
20548                         p = p.parentNode;
20549                     }
20550                 }
20551             }
20552
20553             return false;
20554         }
20555
20556     };
20557
20558 }();
20559
20560 // shorter alias, save a few bytes
20561 Roo.dd.DDM = Roo.dd.DragDropMgr;
20562 Roo.dd.DDM._addListeners();
20563
20564 }/*
20565  * Based on:
20566  * Ext JS Library 1.1.1
20567  * Copyright(c) 2006-2007, Ext JS, LLC.
20568  *
20569  * Originally Released Under LGPL - original licence link has changed is not relivant.
20570  *
20571  * Fork - LGPL
20572  * <script type="text/javascript">
20573  */
20574
20575 /**
20576  * @class Roo.dd.DD
20577  * A DragDrop implementation where the linked element follows the
20578  * mouse cursor during a drag.
20579  * @extends Roo.dd.DragDrop
20580  * @constructor
20581  * @param {String} id the id of the linked element
20582  * @param {String} sGroup the group of related DragDrop items
20583  * @param {object} config an object containing configurable attributes
20584  *                Valid properties for DD:
20585  *                    scroll
20586  */
20587 Roo.dd.DD = function(id, sGroup, config) {
20588     if (id) {
20589         this.init(id, sGroup, config);
20590     }
20591 };
20592
20593 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20594
20595     /**
20596      * When set to true, the utility automatically tries to scroll the browser
20597      * window wehn a drag and drop element is dragged near the viewport boundary.
20598      * Defaults to true.
20599      * @property scroll
20600      * @type boolean
20601      */
20602     scroll: true,
20603
20604     /**
20605      * Sets the pointer offset to the distance between the linked element's top
20606      * left corner and the location the element was clicked
20607      * @method autoOffset
20608      * @param {int} iPageX the X coordinate of the click
20609      * @param {int} iPageY the Y coordinate of the click
20610      */
20611     autoOffset: function(iPageX, iPageY) {
20612         var x = iPageX - this.startPageX;
20613         var y = iPageY - this.startPageY;
20614         this.setDelta(x, y);
20615     },
20616
20617     /**
20618      * Sets the pointer offset.  You can call this directly to force the
20619      * offset to be in a particular location (e.g., pass in 0,0 to set it
20620      * to the center of the object)
20621      * @method setDelta
20622      * @param {int} iDeltaX the distance from the left
20623      * @param {int} iDeltaY the distance from the top
20624      */
20625     setDelta: function(iDeltaX, iDeltaY) {
20626         this.deltaX = iDeltaX;
20627         this.deltaY = iDeltaY;
20628     },
20629
20630     /**
20631      * Sets the drag element to the location of the mousedown or click event,
20632      * maintaining the cursor location relative to the location on the element
20633      * that was clicked.  Override this if you want to place the element in a
20634      * location other than where the cursor is.
20635      * @method setDragElPos
20636      * @param {int} iPageX the X coordinate of the mousedown or drag event
20637      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20638      */
20639     setDragElPos: function(iPageX, iPageY) {
20640         // the first time we do this, we are going to check to make sure
20641         // the element has css positioning
20642
20643         var el = this.getDragEl();
20644         this.alignElWithMouse(el, iPageX, iPageY);
20645     },
20646
20647     /**
20648      * Sets the element to the location of the mousedown or click event,
20649      * maintaining the cursor location relative to the location on the element
20650      * that was clicked.  Override this if you want to place the element in a
20651      * location other than where the cursor is.
20652      * @method alignElWithMouse
20653      * @param {HTMLElement} el the element to move
20654      * @param {int} iPageX the X coordinate of the mousedown or drag event
20655      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20656      */
20657     alignElWithMouse: function(el, iPageX, iPageY) {
20658         var oCoord = this.getTargetCoord(iPageX, iPageY);
20659         var fly = el.dom ? el : Roo.fly(el);
20660         if (!this.deltaSetXY) {
20661             var aCoord = [oCoord.x, oCoord.y];
20662             fly.setXY(aCoord);
20663             var newLeft = fly.getLeft(true);
20664             var newTop  = fly.getTop(true);
20665             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20666         } else {
20667             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20668         }
20669
20670         this.cachePosition(oCoord.x, oCoord.y);
20671         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20672         return oCoord;
20673     },
20674
20675     /**
20676      * Saves the most recent position so that we can reset the constraints and
20677      * tick marks on-demand.  We need to know this so that we can calculate the
20678      * number of pixels the element is offset from its original position.
20679      * @method cachePosition
20680      * @param iPageX the current x position (optional, this just makes it so we
20681      * don't have to look it up again)
20682      * @param iPageY the current y position (optional, this just makes it so we
20683      * don't have to look it up again)
20684      */
20685     cachePosition: function(iPageX, iPageY) {
20686         if (iPageX) {
20687             this.lastPageX = iPageX;
20688             this.lastPageY = iPageY;
20689         } else {
20690             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20691             this.lastPageX = aCoord[0];
20692             this.lastPageY = aCoord[1];
20693         }
20694     },
20695
20696     /**
20697      * Auto-scroll the window if the dragged object has been moved beyond the
20698      * visible window boundary.
20699      * @method autoScroll
20700      * @param {int} x the drag element's x position
20701      * @param {int} y the drag element's y position
20702      * @param {int} h the height of the drag element
20703      * @param {int} w the width of the drag element
20704      * @private
20705      */
20706     autoScroll: function(x, y, h, w) {
20707
20708         if (this.scroll) {
20709             // The client height
20710             var clientH = Roo.lib.Dom.getViewWidth();
20711
20712             // The client width
20713             var clientW = Roo.lib.Dom.getViewHeight();
20714
20715             // The amt scrolled down
20716             var st = this.DDM.getScrollTop();
20717
20718             // The amt scrolled right
20719             var sl = this.DDM.getScrollLeft();
20720
20721             // Location of the bottom of the element
20722             var bot = h + y;
20723
20724             // Location of the right of the element
20725             var right = w + x;
20726
20727             // The distance from the cursor to the bottom of the visible area,
20728             // adjusted so that we don't scroll if the cursor is beyond the
20729             // element drag constraints
20730             var toBot = (clientH + st - y - this.deltaY);
20731
20732             // The distance from the cursor to the right of the visible area
20733             var toRight = (clientW + sl - x - this.deltaX);
20734
20735
20736             // How close to the edge the cursor must be before we scroll
20737             // var thresh = (document.all) ? 100 : 40;
20738             var thresh = 40;
20739
20740             // How many pixels to scroll per autoscroll op.  This helps to reduce
20741             // clunky scrolling. IE is more sensitive about this ... it needs this
20742             // value to be higher.
20743             var scrAmt = (document.all) ? 80 : 30;
20744
20745             // Scroll down if we are near the bottom of the visible page and the
20746             // obj extends below the crease
20747             if ( bot > clientH && toBot < thresh ) {
20748                 window.scrollTo(sl, st + scrAmt);
20749             }
20750
20751             // Scroll up if the window is scrolled down and the top of the object
20752             // goes above the top border
20753             if ( y < st && st > 0 && y - st < thresh ) {
20754                 window.scrollTo(sl, st - scrAmt);
20755             }
20756
20757             // Scroll right if the obj is beyond the right border and the cursor is
20758             // near the border.
20759             if ( right > clientW && toRight < thresh ) {
20760                 window.scrollTo(sl + scrAmt, st);
20761             }
20762
20763             // Scroll left if the window has been scrolled to the right and the obj
20764             // extends past the left border
20765             if ( x < sl && sl > 0 && x - sl < thresh ) {
20766                 window.scrollTo(sl - scrAmt, st);
20767             }
20768         }
20769     },
20770
20771     /**
20772      * Finds the location the element should be placed if we want to move
20773      * it to where the mouse location less the click offset would place us.
20774      * @method getTargetCoord
20775      * @param {int} iPageX the X coordinate of the click
20776      * @param {int} iPageY the Y coordinate of the click
20777      * @return an object that contains the coordinates (Object.x and Object.y)
20778      * @private
20779      */
20780     getTargetCoord: function(iPageX, iPageY) {
20781
20782
20783         var x = iPageX - this.deltaX;
20784         var y = iPageY - this.deltaY;
20785
20786         if (this.constrainX) {
20787             if (x < this.minX) { x = this.minX; }
20788             if (x > this.maxX) { x = this.maxX; }
20789         }
20790
20791         if (this.constrainY) {
20792             if (y < this.minY) { y = this.minY; }
20793             if (y > this.maxY) { y = this.maxY; }
20794         }
20795
20796         x = this.getTick(x, this.xTicks);
20797         y = this.getTick(y, this.yTicks);
20798
20799
20800         return {x:x, y:y};
20801     },
20802
20803     /*
20804      * Sets up config options specific to this class. Overrides
20805      * Roo.dd.DragDrop, but all versions of this method through the
20806      * inheritance chain are called
20807      */
20808     applyConfig: function() {
20809         Roo.dd.DD.superclass.applyConfig.call(this);
20810         this.scroll = (this.config.scroll !== false);
20811     },
20812
20813     /*
20814      * Event that fires prior to the onMouseDown event.  Overrides
20815      * Roo.dd.DragDrop.
20816      */
20817     b4MouseDown: function(e) {
20818         // this.resetConstraints();
20819         this.autoOffset(e.getPageX(),
20820                             e.getPageY());
20821     },
20822
20823     /*
20824      * Event that fires prior to the onDrag event.  Overrides
20825      * Roo.dd.DragDrop.
20826      */
20827     b4Drag: function(e) {
20828         this.setDragElPos(e.getPageX(),
20829                             e.getPageY());
20830     },
20831
20832     toString: function() {
20833         return ("DD " + this.id);
20834     }
20835
20836     //////////////////////////////////////////////////////////////////////////
20837     // Debugging ygDragDrop events that can be overridden
20838     //////////////////////////////////////////////////////////////////////////
20839     /*
20840     startDrag: function(x, y) {
20841     },
20842
20843     onDrag: function(e) {
20844     },
20845
20846     onDragEnter: function(e, id) {
20847     },
20848
20849     onDragOver: function(e, id) {
20850     },
20851
20852     onDragOut: function(e, id) {
20853     },
20854
20855     onDragDrop: function(e, id) {
20856     },
20857
20858     endDrag: function(e) {
20859     }
20860
20861     */
20862
20863 });/*
20864  * Based on:
20865  * Ext JS Library 1.1.1
20866  * Copyright(c) 2006-2007, Ext JS, LLC.
20867  *
20868  * Originally Released Under LGPL - original licence link has changed is not relivant.
20869  *
20870  * Fork - LGPL
20871  * <script type="text/javascript">
20872  */
20873
20874 /**
20875  * @class Roo.dd.DDProxy
20876  * A DragDrop implementation that inserts an empty, bordered div into
20877  * the document that follows the cursor during drag operations.  At the time of
20878  * the click, the frame div is resized to the dimensions of the linked html
20879  * element, and moved to the exact location of the linked element.
20880  *
20881  * References to the "frame" element refer to the single proxy element that
20882  * was created to be dragged in place of all DDProxy elements on the
20883  * page.
20884  *
20885  * @extends Roo.dd.DD
20886  * @constructor
20887  * @param {String} id the id of the linked html element
20888  * @param {String} sGroup the group of related DragDrop objects
20889  * @param {object} config an object containing configurable attributes
20890  *                Valid properties for DDProxy in addition to those in DragDrop:
20891  *                   resizeFrame, centerFrame, dragElId
20892  */
20893 Roo.dd.DDProxy = function(id, sGroup, config) {
20894     if (id) {
20895         this.init(id, sGroup, config);
20896         this.initFrame();
20897     }
20898 };
20899
20900 /**
20901  * The default drag frame div id
20902  * @property Roo.dd.DDProxy.dragElId
20903  * @type String
20904  * @static
20905  */
20906 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20907
20908 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20909
20910     /**
20911      * By default we resize the drag frame to be the same size as the element
20912      * we want to drag (this is to get the frame effect).  We can turn it off
20913      * if we want a different behavior.
20914      * @property resizeFrame
20915      * @type boolean
20916      */
20917     resizeFrame: true,
20918
20919     /**
20920      * By default the frame is positioned exactly where the drag element is, so
20921      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20922      * you do not have constraints on the obj is to have the drag frame centered
20923      * around the cursor.  Set centerFrame to true for this effect.
20924      * @property centerFrame
20925      * @type boolean
20926      */
20927     centerFrame: false,
20928
20929     /**
20930      * Creates the proxy element if it does not yet exist
20931      * @method createFrame
20932      */
20933     createFrame: function() {
20934         var self = this;
20935         var body = document.body;
20936
20937         if (!body || !body.firstChild) {
20938             setTimeout( function() { self.createFrame(); }, 50 );
20939             return;
20940         }
20941
20942         var div = this.getDragEl();
20943
20944         if (!div) {
20945             div    = document.createElement("div");
20946             div.id = this.dragElId;
20947             var s  = div.style;
20948
20949             s.position   = "absolute";
20950             s.visibility = "hidden";
20951             s.cursor     = "move";
20952             s.border     = "2px solid #aaa";
20953             s.zIndex     = 999;
20954
20955             // appendChild can blow up IE if invoked prior to the window load event
20956             // while rendering a table.  It is possible there are other scenarios
20957             // that would cause this to happen as well.
20958             body.insertBefore(div, body.firstChild);
20959         }
20960     },
20961
20962     /**
20963      * Initialization for the drag frame element.  Must be called in the
20964      * constructor of all subclasses
20965      * @method initFrame
20966      */
20967     initFrame: function() {
20968         this.createFrame();
20969     },
20970
20971     applyConfig: function() {
20972         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20973
20974         this.resizeFrame = (this.config.resizeFrame !== false);
20975         this.centerFrame = (this.config.centerFrame);
20976         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20977     },
20978
20979     /**
20980      * Resizes the drag frame to the dimensions of the clicked object, positions
20981      * it over the object, and finally displays it
20982      * @method showFrame
20983      * @param {int} iPageX X click position
20984      * @param {int} iPageY Y click position
20985      * @private
20986      */
20987     showFrame: function(iPageX, iPageY) {
20988         var el = this.getEl();
20989         var dragEl = this.getDragEl();
20990         var s = dragEl.style;
20991
20992         this._resizeProxy();
20993
20994         if (this.centerFrame) {
20995             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20996                            Math.round(parseInt(s.height, 10)/2) );
20997         }
20998
20999         this.setDragElPos(iPageX, iPageY);
21000
21001         Roo.fly(dragEl).show();
21002     },
21003
21004     /**
21005      * The proxy is automatically resized to the dimensions of the linked
21006      * element when a drag is initiated, unless resizeFrame is set to false
21007      * @method _resizeProxy
21008      * @private
21009      */
21010     _resizeProxy: function() {
21011         if (this.resizeFrame) {
21012             var el = this.getEl();
21013             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21014         }
21015     },
21016
21017     // overrides Roo.dd.DragDrop
21018     b4MouseDown: function(e) {
21019         var x = e.getPageX();
21020         var y = e.getPageY();
21021         this.autoOffset(x, y);
21022         this.setDragElPos(x, y);
21023     },
21024
21025     // overrides Roo.dd.DragDrop
21026     b4StartDrag: function(x, y) {
21027         // show the drag frame
21028         this.showFrame(x, y);
21029     },
21030
21031     // overrides Roo.dd.DragDrop
21032     b4EndDrag: function(e) {
21033         Roo.fly(this.getDragEl()).hide();
21034     },
21035
21036     // overrides Roo.dd.DragDrop
21037     // By default we try to move the element to the last location of the frame.
21038     // This is so that the default behavior mirrors that of Roo.dd.DD.
21039     endDrag: function(e) {
21040
21041         var lel = this.getEl();
21042         var del = this.getDragEl();
21043
21044         // Show the drag frame briefly so we can get its position
21045         del.style.visibility = "";
21046
21047         this.beforeMove();
21048         // Hide the linked element before the move to get around a Safari
21049         // rendering bug.
21050         lel.style.visibility = "hidden";
21051         Roo.dd.DDM.moveToEl(lel, del);
21052         del.style.visibility = "hidden";
21053         lel.style.visibility = "";
21054
21055         this.afterDrag();
21056     },
21057
21058     beforeMove : function(){
21059
21060     },
21061
21062     afterDrag : function(){
21063
21064     },
21065
21066     toString: function() {
21067         return ("DDProxy " + this.id);
21068     }
21069
21070 });
21071 /*
21072  * Based on:
21073  * Ext JS Library 1.1.1
21074  * Copyright(c) 2006-2007, Ext JS, LLC.
21075  *
21076  * Originally Released Under LGPL - original licence link has changed is not relivant.
21077  *
21078  * Fork - LGPL
21079  * <script type="text/javascript">
21080  */
21081
21082  /**
21083  * @class Roo.dd.DDTarget
21084  * A DragDrop implementation that does not move, but can be a drop
21085  * target.  You would get the same result by simply omitting implementation
21086  * for the event callbacks, but this way we reduce the processing cost of the
21087  * event listener and the callbacks.
21088  * @extends Roo.dd.DragDrop
21089  * @constructor
21090  * @param {String} id the id of the element that is a drop target
21091  * @param {String} sGroup the group of related DragDrop objects
21092  * @param {object} config an object containing configurable attributes
21093  *                 Valid properties for DDTarget in addition to those in
21094  *                 DragDrop:
21095  *                    none
21096  */
21097 Roo.dd.DDTarget = function(id, sGroup, config) {
21098     if (id) {
21099         this.initTarget(id, sGroup, config);
21100     }
21101     if (config && (config.listeners || config.events)) { 
21102         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21103             listeners : config.listeners || {}, 
21104             events : config.events || {} 
21105         });    
21106     }
21107 };
21108
21109 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21110 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21111     toString: function() {
21112         return ("DDTarget " + this.id);
21113     }
21114 });
21115 /*
21116  * Based on:
21117  * Ext JS Library 1.1.1
21118  * Copyright(c) 2006-2007, Ext JS, LLC.
21119  *
21120  * Originally Released Under LGPL - original licence link has changed is not relivant.
21121  *
21122  * Fork - LGPL
21123  * <script type="text/javascript">
21124  */
21125  
21126
21127 /**
21128  * @class Roo.dd.ScrollManager
21129  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21130  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21131  * @singleton
21132  */
21133 Roo.dd.ScrollManager = function(){
21134     var ddm = Roo.dd.DragDropMgr;
21135     var els = {};
21136     var dragEl = null;
21137     var proc = {};
21138     
21139     
21140     
21141     var onStop = function(e){
21142         dragEl = null;
21143         clearProc();
21144     };
21145     
21146     var triggerRefresh = function(){
21147         if(ddm.dragCurrent){
21148              ddm.refreshCache(ddm.dragCurrent.groups);
21149         }
21150     };
21151     
21152     var doScroll = function(){
21153         if(ddm.dragCurrent){
21154             var dds = Roo.dd.ScrollManager;
21155             if(!dds.animate){
21156                 if(proc.el.scroll(proc.dir, dds.increment)){
21157                     triggerRefresh();
21158                 }
21159             }else{
21160                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21161             }
21162         }
21163     };
21164     
21165     var clearProc = function(){
21166         if(proc.id){
21167             clearInterval(proc.id);
21168         }
21169         proc.id = 0;
21170         proc.el = null;
21171         proc.dir = "";
21172     };
21173     
21174     var startProc = function(el, dir){
21175          Roo.log('scroll startproc');
21176         clearProc();
21177         proc.el = el;
21178         proc.dir = dir;
21179         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21180     };
21181     
21182     var onFire = function(e, isDrop){
21183        
21184         if(isDrop || !ddm.dragCurrent){ return; }
21185         var dds = Roo.dd.ScrollManager;
21186         if(!dragEl || dragEl != ddm.dragCurrent){
21187             dragEl = ddm.dragCurrent;
21188             // refresh regions on drag start
21189             dds.refreshCache();
21190         }
21191         
21192         var xy = Roo.lib.Event.getXY(e);
21193         var pt = new Roo.lib.Point(xy[0], xy[1]);
21194         for(var id in els){
21195             var el = els[id], r = el._region;
21196             if(r && r.contains(pt) && el.isScrollable()){
21197                 if(r.bottom - pt.y <= dds.thresh){
21198                     if(proc.el != el){
21199                         startProc(el, "down");
21200                     }
21201                     return;
21202                 }else if(r.right - pt.x <= dds.thresh){
21203                     if(proc.el != el){
21204                         startProc(el, "left");
21205                     }
21206                     return;
21207                 }else if(pt.y - r.top <= dds.thresh){
21208                     if(proc.el != el){
21209                         startProc(el, "up");
21210                     }
21211                     return;
21212                 }else if(pt.x - r.left <= dds.thresh){
21213                     if(proc.el != el){
21214                         startProc(el, "right");
21215                     }
21216                     return;
21217                 }
21218             }
21219         }
21220         clearProc();
21221     };
21222     
21223     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21224     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21225     
21226     return {
21227         /**
21228          * Registers new overflow element(s) to auto scroll
21229          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21230          */
21231         register : function(el){
21232             if(el instanceof Array){
21233                 for(var i = 0, len = el.length; i < len; i++) {
21234                         this.register(el[i]);
21235                 }
21236             }else{
21237                 el = Roo.get(el);
21238                 els[el.id] = el;
21239             }
21240             Roo.dd.ScrollManager.els = els;
21241         },
21242         
21243         /**
21244          * Unregisters overflow element(s) so they are no longer scrolled
21245          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21246          */
21247         unregister : function(el){
21248             if(el instanceof Array){
21249                 for(var i = 0, len = el.length; i < len; i++) {
21250                         this.unregister(el[i]);
21251                 }
21252             }else{
21253                 el = Roo.get(el);
21254                 delete els[el.id];
21255             }
21256         },
21257         
21258         /**
21259          * The number of pixels from the edge of a container the pointer needs to be to 
21260          * trigger scrolling (defaults to 25)
21261          * @type Number
21262          */
21263         thresh : 25,
21264         
21265         /**
21266          * The number of pixels to scroll in each scroll increment (defaults to 50)
21267          * @type Number
21268          */
21269         increment : 100,
21270         
21271         /**
21272          * The frequency of scrolls in milliseconds (defaults to 500)
21273          * @type Number
21274          */
21275         frequency : 500,
21276         
21277         /**
21278          * True to animate the scroll (defaults to true)
21279          * @type Boolean
21280          */
21281         animate: true,
21282         
21283         /**
21284          * The animation duration in seconds - 
21285          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21286          * @type Number
21287          */
21288         animDuration: .4,
21289         
21290         /**
21291          * Manually trigger a cache refresh.
21292          */
21293         refreshCache : function(){
21294             for(var id in els){
21295                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21296                     els[id]._region = els[id].getRegion();
21297                 }
21298             }
21299         }
21300     };
21301 }();/*
21302  * Based on:
21303  * Ext JS Library 1.1.1
21304  * Copyright(c) 2006-2007, Ext JS, LLC.
21305  *
21306  * Originally Released Under LGPL - original licence link has changed is not relivant.
21307  *
21308  * Fork - LGPL
21309  * <script type="text/javascript">
21310  */
21311  
21312
21313 /**
21314  * @class Roo.dd.Registry
21315  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21316  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21317  * @singleton
21318  */
21319 Roo.dd.Registry = function(){
21320     var elements = {}; 
21321     var handles = {}; 
21322     var autoIdSeed = 0;
21323
21324     var getId = function(el, autogen){
21325         if(typeof el == "string"){
21326             return el;
21327         }
21328         var id = el.id;
21329         if(!id && autogen !== false){
21330             id = "roodd-" + (++autoIdSeed);
21331             el.id = id;
21332         }
21333         return id;
21334     };
21335     
21336     return {
21337     /**
21338      * Register a drag drop element
21339      * @param {String|HTMLElement} element The id or DOM node to register
21340      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21341      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21342      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21343      * populated in the data object (if applicable):
21344      * <pre>
21345 Value      Description<br />
21346 ---------  ------------------------------------------<br />
21347 handles    Array of DOM nodes that trigger dragging<br />
21348            for the element being registered<br />
21349 isHandle   True if the element passed in triggers<br />
21350            dragging itself, else false
21351 </pre>
21352      */
21353         register : function(el, data){
21354             data = data || {};
21355             if(typeof el == "string"){
21356                 el = document.getElementById(el);
21357             }
21358             data.ddel = el;
21359             elements[getId(el)] = data;
21360             if(data.isHandle !== false){
21361                 handles[data.ddel.id] = data;
21362             }
21363             if(data.handles){
21364                 var hs = data.handles;
21365                 for(var i = 0, len = hs.length; i < len; i++){
21366                         handles[getId(hs[i])] = data;
21367                 }
21368             }
21369         },
21370
21371     /**
21372      * Unregister a drag drop element
21373      * @param {String|HTMLElement}  element The id or DOM node to unregister
21374      */
21375         unregister : function(el){
21376             var id = getId(el, false);
21377             var data = elements[id];
21378             if(data){
21379                 delete elements[id];
21380                 if(data.handles){
21381                     var hs = data.handles;
21382                     for(var i = 0, len = hs.length; i < len; i++){
21383                         delete handles[getId(hs[i], false)];
21384                     }
21385                 }
21386             }
21387         },
21388
21389     /**
21390      * Returns the handle registered for a DOM Node by id
21391      * @param {String|HTMLElement} id The DOM node or id to look up
21392      * @return {Object} handle The custom handle data
21393      */
21394         getHandle : function(id){
21395             if(typeof id != "string"){ // must be element?
21396                 id = id.id;
21397             }
21398             return handles[id];
21399         },
21400
21401     /**
21402      * Returns the handle that is registered for the DOM node that is the target of the event
21403      * @param {Event} e The event
21404      * @return {Object} handle The custom handle data
21405      */
21406         getHandleFromEvent : function(e){
21407             var t = Roo.lib.Event.getTarget(e);
21408             return t ? handles[t.id] : null;
21409         },
21410
21411     /**
21412      * Returns a custom data object that is registered for a DOM node by id
21413      * @param {String|HTMLElement} id The DOM node or id to look up
21414      * @return {Object} data The custom data
21415      */
21416         getTarget : function(id){
21417             if(typeof id != "string"){ // must be element?
21418                 id = id.id;
21419             }
21420             return elements[id];
21421         },
21422
21423     /**
21424      * Returns a custom data object that is registered for the DOM node that is the target of the event
21425      * @param {Event} e The event
21426      * @return {Object} data The custom data
21427      */
21428         getTargetFromEvent : function(e){
21429             var t = Roo.lib.Event.getTarget(e);
21430             return t ? elements[t.id] || handles[t.id] : null;
21431         }
21432     };
21433 }();/*
21434  * Based on:
21435  * Ext JS Library 1.1.1
21436  * Copyright(c) 2006-2007, Ext JS, LLC.
21437  *
21438  * Originally Released Under LGPL - original licence link has changed is not relivant.
21439  *
21440  * Fork - LGPL
21441  * <script type="text/javascript">
21442  */
21443  
21444
21445 /**
21446  * @class Roo.dd.StatusProxy
21447  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21448  * default drag proxy used by all Roo.dd components.
21449  * @constructor
21450  * @param {Object} config
21451  */
21452 Roo.dd.StatusProxy = function(config){
21453     Roo.apply(this, config);
21454     this.id = this.id || Roo.id();
21455     this.el = new Roo.Layer({
21456         dh: {
21457             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21458                 {tag: "div", cls: "x-dd-drop-icon"},
21459                 {tag: "div", cls: "x-dd-drag-ghost"}
21460             ]
21461         }, 
21462         shadow: !config || config.shadow !== false
21463     });
21464     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21465     this.dropStatus = this.dropNotAllowed;
21466 };
21467
21468 Roo.dd.StatusProxy.prototype = {
21469     /**
21470      * @cfg {String} dropAllowed
21471      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21472      */
21473     dropAllowed : "x-dd-drop-ok",
21474     /**
21475      * @cfg {String} dropNotAllowed
21476      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21477      */
21478     dropNotAllowed : "x-dd-drop-nodrop",
21479
21480     /**
21481      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21482      * over the current target element.
21483      * @param {String} cssClass The css class for the new drop status indicator image
21484      */
21485     setStatus : function(cssClass){
21486         cssClass = cssClass || this.dropNotAllowed;
21487         if(this.dropStatus != cssClass){
21488             this.el.replaceClass(this.dropStatus, cssClass);
21489             this.dropStatus = cssClass;
21490         }
21491     },
21492
21493     /**
21494      * Resets the status indicator to the default dropNotAllowed value
21495      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21496      */
21497     reset : function(clearGhost){
21498         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21499         this.dropStatus = this.dropNotAllowed;
21500         if(clearGhost){
21501             this.ghost.update("");
21502         }
21503     },
21504
21505     /**
21506      * Updates the contents of the ghost element
21507      * @param {String} html The html that will replace the current innerHTML of the ghost element
21508      */
21509     update : function(html){
21510         if(typeof html == "string"){
21511             this.ghost.update(html);
21512         }else{
21513             this.ghost.update("");
21514             html.style.margin = "0";
21515             this.ghost.dom.appendChild(html);
21516         }
21517         // ensure float = none set?? cant remember why though.
21518         var el = this.ghost.dom.firstChild;
21519                 if(el){
21520                         Roo.fly(el).setStyle('float', 'none');
21521                 }
21522     },
21523     
21524     /**
21525      * Returns the underlying proxy {@link Roo.Layer}
21526      * @return {Roo.Layer} el
21527     */
21528     getEl : function(){
21529         return this.el;
21530     },
21531
21532     /**
21533      * Returns the ghost element
21534      * @return {Roo.Element} el
21535      */
21536     getGhost : function(){
21537         return this.ghost;
21538     },
21539
21540     /**
21541      * Hides the proxy
21542      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21543      */
21544     hide : function(clear){
21545         this.el.hide();
21546         if(clear){
21547             this.reset(true);
21548         }
21549     },
21550
21551     /**
21552      * Stops the repair animation if it's currently running
21553      */
21554     stop : function(){
21555         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21556             this.anim.stop();
21557         }
21558     },
21559
21560     /**
21561      * Displays this proxy
21562      */
21563     show : function(){
21564         this.el.show();
21565     },
21566
21567     /**
21568      * Force the Layer to sync its shadow and shim positions to the element
21569      */
21570     sync : function(){
21571         this.el.sync();
21572     },
21573
21574     /**
21575      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21576      * invalid drop operation by the item being dragged.
21577      * @param {Array} xy The XY position of the element ([x, y])
21578      * @param {Function} callback The function to call after the repair is complete
21579      * @param {Object} scope The scope in which to execute the callback
21580      */
21581     repair : function(xy, callback, scope){
21582         this.callback = callback;
21583         this.scope = scope;
21584         if(xy && this.animRepair !== false){
21585             this.el.addClass("x-dd-drag-repair");
21586             this.el.hideUnders(true);
21587             this.anim = this.el.shift({
21588                 duration: this.repairDuration || .5,
21589                 easing: 'easeOut',
21590                 xy: xy,
21591                 stopFx: true,
21592                 callback: this.afterRepair,
21593                 scope: this
21594             });
21595         }else{
21596             this.afterRepair();
21597         }
21598     },
21599
21600     // private
21601     afterRepair : function(){
21602         this.hide(true);
21603         if(typeof this.callback == "function"){
21604             this.callback.call(this.scope || this);
21605         }
21606         this.callback = null;
21607         this.scope = null;
21608     }
21609 };/*
21610  * Based on:
21611  * Ext JS Library 1.1.1
21612  * Copyright(c) 2006-2007, Ext JS, LLC.
21613  *
21614  * Originally Released Under LGPL - original licence link has changed is not relivant.
21615  *
21616  * Fork - LGPL
21617  * <script type="text/javascript">
21618  */
21619
21620 /**
21621  * @class Roo.dd.DragSource
21622  * @extends Roo.dd.DDProxy
21623  * A simple class that provides the basic implementation needed to make any element draggable.
21624  * @constructor
21625  * @param {String/HTMLElement/Element} el The container element
21626  * @param {Object} config
21627  */
21628 Roo.dd.DragSource = function(el, config){
21629     this.el = Roo.get(el);
21630     this.dragData = {};
21631     
21632     Roo.apply(this, config);
21633     
21634     if(!this.proxy){
21635         this.proxy = new Roo.dd.StatusProxy();
21636     }
21637
21638     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21639           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21640     
21641     this.dragging = false;
21642 };
21643
21644 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21645     /**
21646      * @cfg {String} dropAllowed
21647      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21648      */
21649     dropAllowed : "x-dd-drop-ok",
21650     /**
21651      * @cfg {String} dropNotAllowed
21652      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21653      */
21654     dropNotAllowed : "x-dd-drop-nodrop",
21655
21656     /**
21657      * Returns the data object associated with this drag source
21658      * @return {Object} data An object containing arbitrary data
21659      */
21660     getDragData : function(e){
21661         return this.dragData;
21662     },
21663
21664     // private
21665     onDragEnter : function(e, id){
21666         var target = Roo.dd.DragDropMgr.getDDById(id);
21667         this.cachedTarget = target;
21668         if(this.beforeDragEnter(target, e, id) !== false){
21669             if(target.isNotifyTarget){
21670                 var status = target.notifyEnter(this, e, this.dragData);
21671                 this.proxy.setStatus(status);
21672             }else{
21673                 this.proxy.setStatus(this.dropAllowed);
21674             }
21675             
21676             if(this.afterDragEnter){
21677                 /**
21678                  * An empty function by default, but provided so that you can perform a custom action
21679                  * when the dragged item enters the drop target by providing an implementation.
21680                  * @param {Roo.dd.DragDrop} target The drop target
21681                  * @param {Event} e The event object
21682                  * @param {String} id The id of the dragged element
21683                  * @method afterDragEnter
21684                  */
21685                 this.afterDragEnter(target, e, id);
21686             }
21687         }
21688     },
21689
21690     /**
21691      * An empty function by default, but provided so that you can perform a custom action
21692      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21693      * @param {Roo.dd.DragDrop} target The drop target
21694      * @param {Event} e The event object
21695      * @param {String} id The id of the dragged element
21696      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21697      */
21698     beforeDragEnter : function(target, e, id){
21699         return true;
21700     },
21701
21702     // private
21703     alignElWithMouse: function() {
21704         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21705         this.proxy.sync();
21706     },
21707
21708     // private
21709     onDragOver : function(e, id){
21710         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21711         if(this.beforeDragOver(target, e, id) !== false){
21712             if(target.isNotifyTarget){
21713                 var status = target.notifyOver(this, e, this.dragData);
21714                 this.proxy.setStatus(status);
21715             }
21716
21717             if(this.afterDragOver){
21718                 /**
21719                  * An empty function by default, but provided so that you can perform a custom action
21720                  * while the dragged item is over the drop target by providing an implementation.
21721                  * @param {Roo.dd.DragDrop} target The drop target
21722                  * @param {Event} e The event object
21723                  * @param {String} id The id of the dragged element
21724                  * @method afterDragOver
21725                  */
21726                 this.afterDragOver(target, e, id);
21727             }
21728         }
21729     },
21730
21731     /**
21732      * An empty function by default, but provided so that you can perform a custom action
21733      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21734      * @param {Roo.dd.DragDrop} target The drop target
21735      * @param {Event} e The event object
21736      * @param {String} id The id of the dragged element
21737      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21738      */
21739     beforeDragOver : function(target, e, id){
21740         return true;
21741     },
21742
21743     // private
21744     onDragOut : function(e, id){
21745         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21746         if(this.beforeDragOut(target, e, id) !== false){
21747             if(target.isNotifyTarget){
21748                 target.notifyOut(this, e, this.dragData);
21749             }
21750             this.proxy.reset();
21751             if(this.afterDragOut){
21752                 /**
21753                  * An empty function by default, but provided so that you can perform a custom action
21754                  * after the dragged item is dragged out of the target without dropping.
21755                  * @param {Roo.dd.DragDrop} target The drop target
21756                  * @param {Event} e The event object
21757                  * @param {String} id The id of the dragged element
21758                  * @method afterDragOut
21759                  */
21760                 this.afterDragOut(target, e, id);
21761             }
21762         }
21763         this.cachedTarget = null;
21764     },
21765
21766     /**
21767      * An empty function by default, but provided so that you can perform a custom action before the dragged
21768      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21769      * @param {Roo.dd.DragDrop} target The drop target
21770      * @param {Event} e The event object
21771      * @param {String} id The id of the dragged element
21772      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21773      */
21774     beforeDragOut : function(target, e, id){
21775         return true;
21776     },
21777     
21778     // private
21779     onDragDrop : function(e, id){
21780         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21781         if(this.beforeDragDrop(target, e, id) !== false){
21782             if(target.isNotifyTarget){
21783                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21784                     this.onValidDrop(target, e, id);
21785                 }else{
21786                     this.onInvalidDrop(target, e, id);
21787                 }
21788             }else{
21789                 this.onValidDrop(target, e, id);
21790             }
21791             
21792             if(this.afterDragDrop){
21793                 /**
21794                  * An empty function by default, but provided so that you can perform a custom action
21795                  * after a valid drag drop has occurred by providing an implementation.
21796                  * @param {Roo.dd.DragDrop} target The drop target
21797                  * @param {Event} e The event object
21798                  * @param {String} id The id of the dropped element
21799                  * @method afterDragDrop
21800                  */
21801                 this.afterDragDrop(target, e, id);
21802             }
21803         }
21804         delete this.cachedTarget;
21805     },
21806
21807     /**
21808      * An empty function by default, but provided so that you can perform a custom action before the dragged
21809      * item is dropped onto the target and optionally cancel the onDragDrop.
21810      * @param {Roo.dd.DragDrop} target The drop target
21811      * @param {Event} e The event object
21812      * @param {String} id The id of the dragged element
21813      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21814      */
21815     beforeDragDrop : function(target, e, id){
21816         return true;
21817     },
21818
21819     // private
21820     onValidDrop : function(target, e, id){
21821         this.hideProxy();
21822         if(this.afterValidDrop){
21823             /**
21824              * An empty function by default, but provided so that you can perform a custom action
21825              * after a valid drop has occurred by providing an implementation.
21826              * @param {Object} target The target DD 
21827              * @param {Event} e The event object
21828              * @param {String} id The id of the dropped element
21829              * @method afterInvalidDrop
21830              */
21831             this.afterValidDrop(target, e, id);
21832         }
21833     },
21834
21835     // private
21836     getRepairXY : function(e, data){
21837         return this.el.getXY();  
21838     },
21839
21840     // private
21841     onInvalidDrop : function(target, e, id){
21842         this.beforeInvalidDrop(target, e, id);
21843         if(this.cachedTarget){
21844             if(this.cachedTarget.isNotifyTarget){
21845                 this.cachedTarget.notifyOut(this, e, this.dragData);
21846             }
21847             this.cacheTarget = null;
21848         }
21849         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21850
21851         if(this.afterInvalidDrop){
21852             /**
21853              * An empty function by default, but provided so that you can perform a custom action
21854              * after an invalid drop has occurred by providing an implementation.
21855              * @param {Event} e The event object
21856              * @param {String} id The id of the dropped element
21857              * @method afterInvalidDrop
21858              */
21859             this.afterInvalidDrop(e, id);
21860         }
21861     },
21862
21863     // private
21864     afterRepair : function(){
21865         if(Roo.enableFx){
21866             this.el.highlight(this.hlColor || "c3daf9");
21867         }
21868         this.dragging = false;
21869     },
21870
21871     /**
21872      * An empty function by default, but provided so that you can perform a custom action after an invalid
21873      * drop has occurred.
21874      * @param {Roo.dd.DragDrop} target The drop target
21875      * @param {Event} e The event object
21876      * @param {String} id The id of the dragged element
21877      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21878      */
21879     beforeInvalidDrop : function(target, e, id){
21880         return true;
21881     },
21882
21883     // private
21884     handleMouseDown : function(e){
21885         if(this.dragging) {
21886             return;
21887         }
21888         var data = this.getDragData(e);
21889         if(data && this.onBeforeDrag(data, e) !== false){
21890             this.dragData = data;
21891             this.proxy.stop();
21892             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21893         } 
21894     },
21895
21896     /**
21897      * An empty function by default, but provided so that you can perform a custom action before the initial
21898      * drag event begins and optionally cancel it.
21899      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21900      * @param {Event} e The event object
21901      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21902      */
21903     onBeforeDrag : function(data, e){
21904         return true;
21905     },
21906
21907     /**
21908      * An empty function by default, but provided so that you can perform a custom action once the initial
21909      * drag event has begun.  The drag cannot be canceled from this function.
21910      * @param {Number} x The x position of the click on the dragged object
21911      * @param {Number} y The y position of the click on the dragged object
21912      */
21913     onStartDrag : Roo.emptyFn,
21914
21915     // private - YUI override
21916     startDrag : function(x, y){
21917         this.proxy.reset();
21918         this.dragging = true;
21919         this.proxy.update("");
21920         this.onInitDrag(x, y);
21921         this.proxy.show();
21922     },
21923
21924     // private
21925     onInitDrag : function(x, y){
21926         var clone = this.el.dom.cloneNode(true);
21927         clone.id = Roo.id(); // prevent duplicate ids
21928         this.proxy.update(clone);
21929         this.onStartDrag(x, y);
21930         return true;
21931     },
21932
21933     /**
21934      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21935      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21936      */
21937     getProxy : function(){
21938         return this.proxy;  
21939     },
21940
21941     /**
21942      * Hides the drag source's {@link Roo.dd.StatusProxy}
21943      */
21944     hideProxy : function(){
21945         this.proxy.hide();  
21946         this.proxy.reset(true);
21947         this.dragging = false;
21948     },
21949
21950     // private
21951     triggerCacheRefresh : function(){
21952         Roo.dd.DDM.refreshCache(this.groups);
21953     },
21954
21955     // private - override to prevent hiding
21956     b4EndDrag: function(e) {
21957     },
21958
21959     // private - override to prevent moving
21960     endDrag : function(e){
21961         this.onEndDrag(this.dragData, e);
21962     },
21963
21964     // private
21965     onEndDrag : function(data, e){
21966     },
21967     
21968     // private - pin to cursor
21969     autoOffset : function(x, y) {
21970         this.setDelta(-12, -20);
21971     }    
21972 });/*
21973  * Based on:
21974  * Ext JS Library 1.1.1
21975  * Copyright(c) 2006-2007, Ext JS, LLC.
21976  *
21977  * Originally Released Under LGPL - original licence link has changed is not relivant.
21978  *
21979  * Fork - LGPL
21980  * <script type="text/javascript">
21981  */
21982
21983
21984 /**
21985  * @class Roo.dd.DropTarget
21986  * @extends Roo.dd.DDTarget
21987  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21988  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21989  * @constructor
21990  * @param {String/HTMLElement/Element} el The container element
21991  * @param {Object} config
21992  */
21993 Roo.dd.DropTarget = function(el, config){
21994     this.el = Roo.get(el);
21995     
21996     var listeners = false; ;
21997     if (config && config.listeners) {
21998         listeners= config.listeners;
21999         delete config.listeners;
22000     }
22001     Roo.apply(this, config);
22002     
22003     if(this.containerScroll){
22004         Roo.dd.ScrollManager.register(this.el);
22005     }
22006     this.addEvents( {
22007          /**
22008          * @scope Roo.dd.DropTarget
22009          */
22010          
22011          /**
22012          * @event enter
22013          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22014          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22015          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22016          * 
22017          * IMPORTANT : it should set this.overClass and this.dropAllowed
22018          * 
22019          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22020          * @param {Event} e The event
22021          * @param {Object} data An object containing arbitrary data supplied by the drag source
22022          */
22023         "enter" : true,
22024         
22025          /**
22026          * @event over
22027          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22028          * This method will be called on every mouse movement while the drag source is over the drop target.
22029          * This default implementation simply returns the dropAllowed config value.
22030          * 
22031          * IMPORTANT : it should set this.dropAllowed
22032          * 
22033          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22034          * @param {Event} e The event
22035          * @param {Object} data An object containing arbitrary data supplied by the drag source
22036          
22037          */
22038         "over" : true,
22039         /**
22040          * @event out
22041          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22042          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22043          * overClass (if any) from the drop element.
22044          * 
22045          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22046          * @param {Event} e The event
22047          * @param {Object} data An object containing arbitrary data supplied by the drag source
22048          */
22049          "out" : true,
22050          
22051         /**
22052          * @event drop
22053          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22054          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22055          * implementation that does something to process the drop event and returns true so that the drag source's
22056          * repair action does not run.
22057          * 
22058          * IMPORTANT : it should set this.success
22059          * 
22060          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22061          * @param {Event} e The event
22062          * @param {Object} data An object containing arbitrary data supplied by the drag source
22063         */
22064          "drop" : true
22065     });
22066             
22067      
22068     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22069         this.el.dom, 
22070         this.ddGroup || this.group,
22071         {
22072             isTarget: true,
22073             listeners : listeners || {} 
22074            
22075         
22076         }
22077     );
22078
22079 };
22080
22081 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22082     /**
22083      * @cfg {String} overClass
22084      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22085      */
22086      /**
22087      * @cfg {String} ddGroup
22088      * The drag drop group to handle drop events for
22089      */
22090      
22091     /**
22092      * @cfg {String} dropAllowed
22093      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22094      */
22095     dropAllowed : "x-dd-drop-ok",
22096     /**
22097      * @cfg {String} dropNotAllowed
22098      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22099      */
22100     dropNotAllowed : "x-dd-drop-nodrop",
22101     /**
22102      * @cfg {boolean} success
22103      * set this after drop listener.. 
22104      */
22105     success : false,
22106     /**
22107      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22108      * if the drop point is valid for over/enter..
22109      */
22110     valid : false,
22111     // private
22112     isTarget : true,
22113
22114     // private
22115     isNotifyTarget : true,
22116     
22117     /**
22118      * @hide
22119      */
22120     notifyEnter : function(dd, e, data)
22121     {
22122         this.valid = true;
22123         this.fireEvent('enter', dd, e, data);
22124         if(this.overClass){
22125             this.el.addClass(this.overClass);
22126         }
22127         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22128             this.valid ? this.dropAllowed : this.dropNotAllowed
22129         );
22130     },
22131
22132     /**
22133      * @hide
22134      */
22135     notifyOver : function(dd, e, data)
22136     {
22137         this.valid = true;
22138         this.fireEvent('over', dd, e, data);
22139         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22140             this.valid ? this.dropAllowed : this.dropNotAllowed
22141         );
22142     },
22143
22144     /**
22145      * @hide
22146      */
22147     notifyOut : function(dd, e, data)
22148     {
22149         this.fireEvent('out', dd, e, data);
22150         if(this.overClass){
22151             this.el.removeClass(this.overClass);
22152         }
22153     },
22154
22155     /**
22156      * @hide
22157      */
22158     notifyDrop : function(dd, e, data)
22159     {
22160         this.success = false;
22161         this.fireEvent('drop', dd, e, data);
22162         return this.success;
22163     }
22164 });/*
22165  * Based on:
22166  * Ext JS Library 1.1.1
22167  * Copyright(c) 2006-2007, Ext JS, LLC.
22168  *
22169  * Originally Released Under LGPL - original licence link has changed is not relivant.
22170  *
22171  * Fork - LGPL
22172  * <script type="text/javascript">
22173  */
22174
22175
22176 /**
22177  * @class Roo.dd.DragZone
22178  * @extends Roo.dd.DragSource
22179  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22180  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22181  * @constructor
22182  * @param {String/HTMLElement/Element} el The container element
22183  * @param {Object} config
22184  */
22185 Roo.dd.DragZone = function(el, config){
22186     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22187     if(this.containerScroll){
22188         Roo.dd.ScrollManager.register(this.el);
22189     }
22190 };
22191
22192 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22193     /**
22194      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22195      * for auto scrolling during drag operations.
22196      */
22197     /**
22198      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22199      * method after a failed drop (defaults to "c3daf9" - light blue)
22200      */
22201
22202     /**
22203      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22204      * for a valid target to drag based on the mouse down. Override this method
22205      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22206      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22207      * @param {EventObject} e The mouse down event
22208      * @return {Object} The dragData
22209      */
22210     getDragData : function(e){
22211         return Roo.dd.Registry.getHandleFromEvent(e);
22212     },
22213     
22214     /**
22215      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22216      * this.dragData.ddel
22217      * @param {Number} x The x position of the click on the dragged object
22218      * @param {Number} y The y position of the click on the dragged object
22219      * @return {Boolean} true to continue the drag, false to cancel
22220      */
22221     onInitDrag : function(x, y){
22222         this.proxy.update(this.dragData.ddel.cloneNode(true));
22223         this.onStartDrag(x, y);
22224         return true;
22225     },
22226     
22227     /**
22228      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22229      */
22230     afterRepair : function(){
22231         if(Roo.enableFx){
22232             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22233         }
22234         this.dragging = false;
22235     },
22236
22237     /**
22238      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22239      * the XY of this.dragData.ddel
22240      * @param {EventObject} e The mouse up event
22241      * @return {Array} The xy location (e.g. [100, 200])
22242      */
22243     getRepairXY : function(e){
22244         return Roo.Element.fly(this.dragData.ddel).getXY();  
22245     }
22246 });/*
22247  * Based on:
22248  * Ext JS Library 1.1.1
22249  * Copyright(c) 2006-2007, Ext JS, LLC.
22250  *
22251  * Originally Released Under LGPL - original licence link has changed is not relivant.
22252  *
22253  * Fork - LGPL
22254  * <script type="text/javascript">
22255  */
22256 /**
22257  * @class Roo.dd.DropZone
22258  * @extends Roo.dd.DropTarget
22259  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22260  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22261  * @constructor
22262  * @param {String/HTMLElement/Element} el The container element
22263  * @param {Object} config
22264  */
22265 Roo.dd.DropZone = function(el, config){
22266     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22267 };
22268
22269 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22270     /**
22271      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22272      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22273      * provide your own custom lookup.
22274      * @param {Event} e The event
22275      * @return {Object} data The custom data
22276      */
22277     getTargetFromEvent : function(e){
22278         return Roo.dd.Registry.getTargetFromEvent(e);
22279     },
22280
22281     /**
22282      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22283      * that it has registered.  This method has no default implementation and should be overridden to provide
22284      * node-specific processing if necessary.
22285      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22286      * {@link #getTargetFromEvent} for this node)
22287      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22288      * @param {Event} e The event
22289      * @param {Object} data An object containing arbitrary data supplied by the drag source
22290      */
22291     onNodeEnter : function(n, dd, e, data){
22292         
22293     },
22294
22295     /**
22296      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22297      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22298      * overridden to provide the proper feedback.
22299      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22300      * {@link #getTargetFromEvent} for this node)
22301      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22302      * @param {Event} e The event
22303      * @param {Object} data An object containing arbitrary data supplied by the drag source
22304      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22305      * underlying {@link Roo.dd.StatusProxy} can be updated
22306      */
22307     onNodeOver : function(n, dd, e, data){
22308         return this.dropAllowed;
22309     },
22310
22311     /**
22312      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22313      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22314      * node-specific processing if necessary.
22315      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22316      * {@link #getTargetFromEvent} for this node)
22317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22318      * @param {Event} e The event
22319      * @param {Object} data An object containing arbitrary data supplied by the drag source
22320      */
22321     onNodeOut : function(n, dd, e, data){
22322         
22323     },
22324
22325     /**
22326      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22327      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22328      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22329      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22330      * {@link #getTargetFromEvent} for this node)
22331      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22332      * @param {Event} e The event
22333      * @param {Object} data An object containing arbitrary data supplied by the drag source
22334      * @return {Boolean} True if the drop was valid, else false
22335      */
22336     onNodeDrop : function(n, dd, e, data){
22337         return false;
22338     },
22339
22340     /**
22341      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22342      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22343      * it should be overridden to provide the proper feedback if necessary.
22344      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22345      * @param {Event} e The event
22346      * @param {Object} data An object containing arbitrary data supplied by the drag source
22347      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22348      * underlying {@link Roo.dd.StatusProxy} can be updated
22349      */
22350     onContainerOver : function(dd, e, data){
22351         return this.dropNotAllowed;
22352     },
22353
22354     /**
22355      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22356      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22357      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22358      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22359      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22360      * @param {Event} e The event
22361      * @param {Object} data An object containing arbitrary data supplied by the drag source
22362      * @return {Boolean} True if the drop was valid, else false
22363      */
22364     onContainerDrop : function(dd, e, data){
22365         return false;
22366     },
22367
22368     /**
22369      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22370      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22371      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22372      * you should override this method and provide a custom implementation.
22373      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22374      * @param {Event} e The event
22375      * @param {Object} data An object containing arbitrary data supplied by the drag source
22376      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22377      * underlying {@link Roo.dd.StatusProxy} can be updated
22378      */
22379     notifyEnter : function(dd, e, data){
22380         return this.dropNotAllowed;
22381     },
22382
22383     /**
22384      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22385      * This method will be called on every mouse movement while the drag source is over the drop zone.
22386      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22387      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22388      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22389      * registered node, it will call {@link #onContainerOver}.
22390      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22391      * @param {Event} e The event
22392      * @param {Object} data An object containing arbitrary data supplied by the drag source
22393      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22394      * underlying {@link Roo.dd.StatusProxy} can be updated
22395      */
22396     notifyOver : function(dd, e, data){
22397         var n = this.getTargetFromEvent(e);
22398         if(!n){ // not over valid drop target
22399             if(this.lastOverNode){
22400                 this.onNodeOut(this.lastOverNode, dd, e, data);
22401                 this.lastOverNode = null;
22402             }
22403             return this.onContainerOver(dd, e, data);
22404         }
22405         if(this.lastOverNode != n){
22406             if(this.lastOverNode){
22407                 this.onNodeOut(this.lastOverNode, dd, e, data);
22408             }
22409             this.onNodeEnter(n, dd, e, data);
22410             this.lastOverNode = n;
22411         }
22412         return this.onNodeOver(n, dd, e, data);
22413     },
22414
22415     /**
22416      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22417      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22418      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22419      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22420      * @param {Event} e The event
22421      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22422      */
22423     notifyOut : function(dd, e, data){
22424         if(this.lastOverNode){
22425             this.onNodeOut(this.lastOverNode, dd, e, data);
22426             this.lastOverNode = null;
22427         }
22428     },
22429
22430     /**
22431      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22432      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22433      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22434      * otherwise it will call {@link #onContainerDrop}.
22435      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22436      * @param {Event} e The event
22437      * @param {Object} data An object containing arbitrary data supplied by the drag source
22438      * @return {Boolean} True if the drop was valid, else false
22439      */
22440     notifyDrop : function(dd, e, data){
22441         if(this.lastOverNode){
22442             this.onNodeOut(this.lastOverNode, dd, e, data);
22443             this.lastOverNode = null;
22444         }
22445         var n = this.getTargetFromEvent(e);
22446         return n ?
22447             this.onNodeDrop(n, dd, e, data) :
22448             this.onContainerDrop(dd, e, data);
22449     },
22450
22451     // private
22452     triggerCacheRefresh : function(){
22453         Roo.dd.DDM.refreshCache(this.groups);
22454     }  
22455 });/*
22456  * Based on:
22457  * Ext JS Library 1.1.1
22458  * Copyright(c) 2006-2007, Ext JS, LLC.
22459  *
22460  * Originally Released Under LGPL - original licence link has changed is not relivant.
22461  *
22462  * Fork - LGPL
22463  * <script type="text/javascript">
22464  */
22465
22466
22467 /**
22468  * @class Roo.data.SortTypes
22469  * @singleton
22470  * Defines the default sorting (casting?) comparison functions used when sorting data.
22471  */
22472 Roo.data.SortTypes = {
22473     /**
22474      * Default sort that does nothing
22475      * @param {Mixed} s The value being converted
22476      * @return {Mixed} The comparison value
22477      */
22478     none : function(s){
22479         return s;
22480     },
22481     
22482     /**
22483      * The regular expression used to strip tags
22484      * @type {RegExp}
22485      * @property
22486      */
22487     stripTagsRE : /<\/?[^>]+>/gi,
22488     
22489     /**
22490      * Strips all HTML tags to sort on text only
22491      * @param {Mixed} s The value being converted
22492      * @return {String} The comparison value
22493      */
22494     asText : function(s){
22495         return String(s).replace(this.stripTagsRE, "");
22496     },
22497     
22498     /**
22499      * Strips all HTML tags to sort on text only - Case insensitive
22500      * @param {Mixed} s The value being converted
22501      * @return {String} The comparison value
22502      */
22503     asUCText : function(s){
22504         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22505     },
22506     
22507     /**
22508      * Case insensitive string
22509      * @param {Mixed} s The value being converted
22510      * @return {String} The comparison value
22511      */
22512     asUCString : function(s) {
22513         return String(s).toUpperCase();
22514     },
22515     
22516     /**
22517      * Date sorting
22518      * @param {Mixed} s The value being converted
22519      * @return {Number} The comparison value
22520      */
22521     asDate : function(s) {
22522         if(!s){
22523             return 0;
22524         }
22525         if(s instanceof Date){
22526             return s.getTime();
22527         }
22528         return Date.parse(String(s));
22529     },
22530     
22531     /**
22532      * Float sorting
22533      * @param {Mixed} s The value being converted
22534      * @return {Float} The comparison value
22535      */
22536     asFloat : function(s) {
22537         var val = parseFloat(String(s).replace(/,/g, ""));
22538         if(isNaN(val)) {
22539             val = 0;
22540         }
22541         return val;
22542     },
22543     
22544     /**
22545      * Integer sorting
22546      * @param {Mixed} s The value being converted
22547      * @return {Number} The comparison value
22548      */
22549     asInt : function(s) {
22550         var val = parseInt(String(s).replace(/,/g, ""));
22551         if(isNaN(val)) {
22552             val = 0;
22553         }
22554         return val;
22555     }
22556 };/*
22557  * Based on:
22558  * Ext JS Library 1.1.1
22559  * Copyright(c) 2006-2007, Ext JS, LLC.
22560  *
22561  * Originally Released Under LGPL - original licence link has changed is not relivant.
22562  *
22563  * Fork - LGPL
22564  * <script type="text/javascript">
22565  */
22566
22567 /**
22568 * @class Roo.data.Record
22569  * Instances of this class encapsulate both record <em>definition</em> information, and record
22570  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22571  * to access Records cached in an {@link Roo.data.Store} object.<br>
22572  * <p>
22573  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22574  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22575  * objects.<br>
22576  * <p>
22577  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22578  * @constructor
22579  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22580  * {@link #create}. The parameters are the same.
22581  * @param {Array} data An associative Array of data values keyed by the field name.
22582  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22583  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22584  * not specified an integer id is generated.
22585  */
22586 Roo.data.Record = function(data, id){
22587     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22588     this.data = data;
22589 };
22590
22591 /**
22592  * Generate a constructor for a specific record layout.
22593  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22594  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22595  * Each field definition object may contain the following properties: <ul>
22596  * <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,
22597  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22598  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22599  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22600  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22601  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22602  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22603  * this may be omitted.</p></li>
22604  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22605  * <ul><li>auto (Default, implies no conversion)</li>
22606  * <li>string</li>
22607  * <li>int</li>
22608  * <li>float</li>
22609  * <li>boolean</li>
22610  * <li>date</li></ul></p></li>
22611  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22612  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22613  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22614  * by the Reader into an object that will be stored in the Record. It is passed the
22615  * following parameters:<ul>
22616  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22617  * </ul></p></li>
22618  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22619  * </ul>
22620  * <br>usage:<br><pre><code>
22621 var TopicRecord = Roo.data.Record.create(
22622     {name: 'title', mapping: 'topic_title'},
22623     {name: 'author', mapping: 'username'},
22624     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22625     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22626     {name: 'lastPoster', mapping: 'user2'},
22627     {name: 'excerpt', mapping: 'post_text'}
22628 );
22629
22630 var myNewRecord = new TopicRecord({
22631     title: 'Do my job please',
22632     author: 'noobie',
22633     totalPosts: 1,
22634     lastPost: new Date(),
22635     lastPoster: 'Animal',
22636     excerpt: 'No way dude!'
22637 });
22638 myStore.add(myNewRecord);
22639 </code></pre>
22640  * @method create
22641  * @static
22642  */
22643 Roo.data.Record.create = function(o){
22644     var f = function(){
22645         f.superclass.constructor.apply(this, arguments);
22646     };
22647     Roo.extend(f, Roo.data.Record);
22648     var p = f.prototype;
22649     p.fields = new Roo.util.MixedCollection(false, function(field){
22650         return field.name;
22651     });
22652     for(var i = 0, len = o.length; i < len; i++){
22653         p.fields.add(new Roo.data.Field(o[i]));
22654     }
22655     f.getField = function(name){
22656         return p.fields.get(name);  
22657     };
22658     return f;
22659 };
22660
22661 Roo.data.Record.AUTO_ID = 1000;
22662 Roo.data.Record.EDIT = 'edit';
22663 Roo.data.Record.REJECT = 'reject';
22664 Roo.data.Record.COMMIT = 'commit';
22665
22666 Roo.data.Record.prototype = {
22667     /**
22668      * Readonly flag - true if this record has been modified.
22669      * @type Boolean
22670      */
22671     dirty : false,
22672     editing : false,
22673     error: null,
22674     modified: null,
22675
22676     // private
22677     join : function(store){
22678         this.store = store;
22679     },
22680
22681     /**
22682      * Set the named field to the specified value.
22683      * @param {String} name The name of the field to set.
22684      * @param {Object} value The value to set the field to.
22685      */
22686     set : function(name, value){
22687         if(this.data[name] == value){
22688             return;
22689         }
22690         this.dirty = true;
22691         if(!this.modified){
22692             this.modified = {};
22693         }
22694         if(typeof this.modified[name] == 'undefined'){
22695             this.modified[name] = this.data[name];
22696         }
22697         this.data[name] = value;
22698         if(!this.editing && this.store){
22699             this.store.afterEdit(this);
22700         }       
22701     },
22702
22703     /**
22704      * Get the value of the named field.
22705      * @param {String} name The name of the field to get the value of.
22706      * @return {Object} The value of the field.
22707      */
22708     get : function(name){
22709         return this.data[name]; 
22710     },
22711
22712     // private
22713     beginEdit : function(){
22714         this.editing = true;
22715         this.modified = {}; 
22716     },
22717
22718     // private
22719     cancelEdit : function(){
22720         this.editing = false;
22721         delete this.modified;
22722     },
22723
22724     // private
22725     endEdit : function(){
22726         this.editing = false;
22727         if(this.dirty && this.store){
22728             this.store.afterEdit(this);
22729         }
22730     },
22731
22732     /**
22733      * Usually called by the {@link Roo.data.Store} which owns the Record.
22734      * Rejects all changes made to the Record since either creation, or the last commit operation.
22735      * Modified fields are reverted to their original values.
22736      * <p>
22737      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22738      * of reject operations.
22739      */
22740     reject : function(){
22741         var m = this.modified;
22742         for(var n in m){
22743             if(typeof m[n] != "function"){
22744                 this.data[n] = m[n];
22745             }
22746         }
22747         this.dirty = false;
22748         delete this.modified;
22749         this.editing = false;
22750         if(this.store){
22751             this.store.afterReject(this);
22752         }
22753     },
22754
22755     /**
22756      * Usually called by the {@link Roo.data.Store} which owns the Record.
22757      * Commits all changes made to the Record since either creation, or the last commit operation.
22758      * <p>
22759      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22760      * of commit operations.
22761      */
22762     commit : function(){
22763         this.dirty = false;
22764         delete this.modified;
22765         this.editing = false;
22766         if(this.store){
22767             this.store.afterCommit(this);
22768         }
22769     },
22770
22771     // private
22772     hasError : function(){
22773         return this.error != null;
22774     },
22775
22776     // private
22777     clearError : function(){
22778         this.error = null;
22779     },
22780
22781     /**
22782      * Creates a copy of this record.
22783      * @param {String} id (optional) A new record id if you don't want to use this record's id
22784      * @return {Record}
22785      */
22786     copy : function(newId) {
22787         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22788     }
22789 };/*
22790  * Based on:
22791  * Ext JS Library 1.1.1
22792  * Copyright(c) 2006-2007, Ext JS, LLC.
22793  *
22794  * Originally Released Under LGPL - original licence link has changed is not relivant.
22795  *
22796  * Fork - LGPL
22797  * <script type="text/javascript">
22798  */
22799
22800
22801
22802 /**
22803  * @class Roo.data.Store
22804  * @extends Roo.util.Observable
22805  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22806  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22807  * <p>
22808  * 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
22809  * has no knowledge of the format of the data returned by the Proxy.<br>
22810  * <p>
22811  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22812  * instances from the data object. These records are cached and made available through accessor functions.
22813  * @constructor
22814  * Creates a new Store.
22815  * @param {Object} config A config object containing the objects needed for the Store to access data,
22816  * and read the data into Records.
22817  */
22818 Roo.data.Store = function(config){
22819     this.data = new Roo.util.MixedCollection(false);
22820     this.data.getKey = function(o){
22821         return o.id;
22822     };
22823     this.baseParams = {};
22824     // private
22825     this.paramNames = {
22826         "start" : "start",
22827         "limit" : "limit",
22828         "sort" : "sort",
22829         "dir" : "dir",
22830         "multisort" : "_multisort"
22831     };
22832
22833     if(config && config.data){
22834         this.inlineData = config.data;
22835         delete config.data;
22836     }
22837
22838     Roo.apply(this, config);
22839     
22840     if(this.reader){ // reader passed
22841         this.reader = Roo.factory(this.reader, Roo.data);
22842         this.reader.xmodule = this.xmodule || false;
22843         if(!this.recordType){
22844             this.recordType = this.reader.recordType;
22845         }
22846         if(this.reader.onMetaChange){
22847             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22848         }
22849     }
22850
22851     if(this.recordType){
22852         this.fields = this.recordType.prototype.fields;
22853     }
22854     this.modified = [];
22855
22856     this.addEvents({
22857         /**
22858          * @event datachanged
22859          * Fires when the data cache has changed, and a widget which is using this Store
22860          * as a Record cache should refresh its view.
22861          * @param {Store} this
22862          */
22863         datachanged : true,
22864         /**
22865          * @event metachange
22866          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22867          * @param {Store} this
22868          * @param {Object} meta The JSON metadata
22869          */
22870         metachange : true,
22871         /**
22872          * @event add
22873          * Fires when Records have been added to the Store
22874          * @param {Store} this
22875          * @param {Roo.data.Record[]} records The array of Records added
22876          * @param {Number} index The index at which the record(s) were added
22877          */
22878         add : true,
22879         /**
22880          * @event remove
22881          * Fires when a Record has been removed from the Store
22882          * @param {Store} this
22883          * @param {Roo.data.Record} record The Record that was removed
22884          * @param {Number} index The index at which the record was removed
22885          */
22886         remove : true,
22887         /**
22888          * @event update
22889          * Fires when a Record has been updated
22890          * @param {Store} this
22891          * @param {Roo.data.Record} record The Record that was updated
22892          * @param {String} operation The update operation being performed.  Value may be one of:
22893          * <pre><code>
22894  Roo.data.Record.EDIT
22895  Roo.data.Record.REJECT
22896  Roo.data.Record.COMMIT
22897          * </code></pre>
22898          */
22899         update : true,
22900         /**
22901          * @event clear
22902          * Fires when the data cache has been cleared.
22903          * @param {Store} this
22904          */
22905         clear : true,
22906         /**
22907          * @event beforeload
22908          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22909          * the load action will be canceled.
22910          * @param {Store} this
22911          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22912          */
22913         beforeload : true,
22914         /**
22915          * @event beforeloadadd
22916          * Fires after a new set of Records has been loaded.
22917          * @param {Store} this
22918          * @param {Roo.data.Record[]} records The Records that were loaded
22919          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22920          */
22921         beforeloadadd : true,
22922         /**
22923          * @event load
22924          * Fires after a new set of Records has been loaded, before they are added to the store.
22925          * @param {Store} this
22926          * @param {Roo.data.Record[]} records The Records that were loaded
22927          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22928          * @params {Object} return from reader
22929          */
22930         load : true,
22931         /**
22932          * @event loadexception
22933          * Fires if an exception occurs in the Proxy during loading.
22934          * Called with the signature of the Proxy's "loadexception" event.
22935          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22936          * 
22937          * @param {Proxy} 
22938          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22939          * @param {Object} load options 
22940          * @param {Object} jsonData from your request (normally this contains the Exception)
22941          */
22942         loadexception : true
22943     });
22944     
22945     if(this.proxy){
22946         this.proxy = Roo.factory(this.proxy, Roo.data);
22947         this.proxy.xmodule = this.xmodule || false;
22948         this.relayEvents(this.proxy,  ["loadexception"]);
22949     }
22950     this.sortToggle = {};
22951     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22952
22953     Roo.data.Store.superclass.constructor.call(this);
22954
22955     if(this.inlineData){
22956         this.loadData(this.inlineData);
22957         delete this.inlineData;
22958     }
22959 };
22960
22961 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22962      /**
22963     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22964     * without a remote query - used by combo/forms at present.
22965     */
22966     
22967     /**
22968     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22969     */
22970     /**
22971     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22972     */
22973     /**
22974     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22975     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22976     */
22977     /**
22978     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22979     * on any HTTP request
22980     */
22981     /**
22982     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22983     */
22984     /**
22985     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22986     */
22987     multiSort: false,
22988     /**
22989     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22990     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22991     */
22992     remoteSort : false,
22993
22994     /**
22995     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22996      * loaded or when a record is removed. (defaults to false).
22997     */
22998     pruneModifiedRecords : false,
22999
23000     // private
23001     lastOptions : null,
23002
23003     /**
23004      * Add Records to the Store and fires the add event.
23005      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23006      */
23007     add : function(records){
23008         records = [].concat(records);
23009         for(var i = 0, len = records.length; i < len; i++){
23010             records[i].join(this);
23011         }
23012         var index = this.data.length;
23013         this.data.addAll(records);
23014         this.fireEvent("add", this, records, index);
23015     },
23016
23017     /**
23018      * Remove a Record from the Store and fires the remove event.
23019      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23020      */
23021     remove : function(record){
23022         var index = this.data.indexOf(record);
23023         this.data.removeAt(index);
23024  
23025         if(this.pruneModifiedRecords){
23026             this.modified.remove(record);
23027         }
23028         this.fireEvent("remove", this, record, index);
23029     },
23030
23031     /**
23032      * Remove all Records from the Store and fires the clear event.
23033      */
23034     removeAll : function(){
23035         this.data.clear();
23036         if(this.pruneModifiedRecords){
23037             this.modified = [];
23038         }
23039         this.fireEvent("clear", this);
23040     },
23041
23042     /**
23043      * Inserts Records to the Store at the given index and fires the add event.
23044      * @param {Number} index The start index at which to insert the passed Records.
23045      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23046      */
23047     insert : function(index, records){
23048         records = [].concat(records);
23049         for(var i = 0, len = records.length; i < len; i++){
23050             this.data.insert(index, records[i]);
23051             records[i].join(this);
23052         }
23053         this.fireEvent("add", this, records, index);
23054     },
23055
23056     /**
23057      * Get the index within the cache of the passed Record.
23058      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23059      * @return {Number} The index of the passed Record. Returns -1 if not found.
23060      */
23061     indexOf : function(record){
23062         return this.data.indexOf(record);
23063     },
23064
23065     /**
23066      * Get the index within the cache of the Record with the passed id.
23067      * @param {String} id The id of the Record to find.
23068      * @return {Number} The index of the Record. Returns -1 if not found.
23069      */
23070     indexOfId : function(id){
23071         return this.data.indexOfKey(id);
23072     },
23073
23074     /**
23075      * Get the Record with the specified id.
23076      * @param {String} id The id of the Record to find.
23077      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23078      */
23079     getById : function(id){
23080         return this.data.key(id);
23081     },
23082
23083     /**
23084      * Get the Record at the specified index.
23085      * @param {Number} index The index of the Record to find.
23086      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23087      */
23088     getAt : function(index){
23089         return this.data.itemAt(index);
23090     },
23091
23092     /**
23093      * Returns a range of Records between specified indices.
23094      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23095      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23096      * @return {Roo.data.Record[]} An array of Records
23097      */
23098     getRange : function(start, end){
23099         return this.data.getRange(start, end);
23100     },
23101
23102     // private
23103     storeOptions : function(o){
23104         o = Roo.apply({}, o);
23105         delete o.callback;
23106         delete o.scope;
23107         this.lastOptions = o;
23108     },
23109
23110     /**
23111      * Loads the Record cache from the configured Proxy using the configured Reader.
23112      * <p>
23113      * If using remote paging, then the first load call must specify the <em>start</em>
23114      * and <em>limit</em> properties in the options.params property to establish the initial
23115      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23116      * <p>
23117      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23118      * and this call will return before the new data has been loaded. Perform any post-processing
23119      * in a callback function, or in a "load" event handler.</strong>
23120      * <p>
23121      * @param {Object} options An object containing properties which control loading options:<ul>
23122      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23123      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23124      * passed the following arguments:<ul>
23125      * <li>r : Roo.data.Record[]</li>
23126      * <li>options: Options object from the load call</li>
23127      * <li>success: Boolean success indicator</li></ul></li>
23128      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23129      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23130      * </ul>
23131      */
23132     load : function(options){
23133         options = options || {};
23134         if(this.fireEvent("beforeload", this, options) !== false){
23135             this.storeOptions(options);
23136             var p = Roo.apply(options.params || {}, this.baseParams);
23137             // if meta was not loaded from remote source.. try requesting it.
23138             if (!this.reader.metaFromRemote) {
23139                 p._requestMeta = 1;
23140             }
23141             if(this.sortInfo && this.remoteSort){
23142                 var pn = this.paramNames;
23143                 p[pn["sort"]] = this.sortInfo.field;
23144                 p[pn["dir"]] = this.sortInfo.direction;
23145             }
23146             if (this.multiSort) {
23147                 var pn = this.paramNames;
23148                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23149             }
23150             
23151             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23152         }
23153     },
23154
23155     /**
23156      * Reloads the Record cache from the configured Proxy using the configured Reader and
23157      * the options from the last load operation performed.
23158      * @param {Object} options (optional) An object containing properties which may override the options
23159      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23160      * the most recently used options are reused).
23161      */
23162     reload : function(options){
23163         this.load(Roo.applyIf(options||{}, this.lastOptions));
23164     },
23165
23166     // private
23167     // Called as a callback by the Reader during a load operation.
23168     loadRecords : function(o, options, success){
23169         if(!o || success === false){
23170             if(success !== false){
23171                 this.fireEvent("load", this, [], options, o);
23172             }
23173             if(options.callback){
23174                 options.callback.call(options.scope || this, [], options, false);
23175             }
23176             return;
23177         }
23178         // if data returned failure - throw an exception.
23179         if (o.success === false) {
23180             // show a message if no listener is registered.
23181             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23182                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23183             }
23184             // loadmask wil be hooked into this..
23185             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23186             return;
23187         }
23188         var r = o.records, t = o.totalRecords || r.length;
23189         
23190         this.fireEvent("beforeloadadd", this, r, options, o);
23191         
23192         if(!options || options.add !== true){
23193             if(this.pruneModifiedRecords){
23194                 this.modified = [];
23195             }
23196             for(var i = 0, len = r.length; i < len; i++){
23197                 r[i].join(this);
23198             }
23199             if(this.snapshot){
23200                 this.data = this.snapshot;
23201                 delete this.snapshot;
23202             }
23203             this.data.clear();
23204             this.data.addAll(r);
23205             this.totalLength = t;
23206             this.applySort();
23207             this.fireEvent("datachanged", this);
23208         }else{
23209             this.totalLength = Math.max(t, this.data.length+r.length);
23210             this.add(r);
23211         }
23212         
23213         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23214                 
23215             var e = new Roo.data.Record({});
23216
23217             e.set(this.parent.displayField, this.parent.emptyTitle);
23218             e.set(this.parent.valueField, '');
23219
23220             this.insert(0, e);
23221         }
23222             
23223         this.fireEvent("load", this, r, options, o);
23224         if(options.callback){
23225             options.callback.call(options.scope || this, r, options, true);
23226         }
23227     },
23228
23229
23230     /**
23231      * Loads data from a passed data block. A Reader which understands the format of the data
23232      * must have been configured in the constructor.
23233      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23234      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23235      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23236      */
23237     loadData : function(o, append){
23238         var r = this.reader.readRecords(o);
23239         this.loadRecords(r, {add: append}, true);
23240     },
23241
23242     /**
23243      * Gets the number of cached records.
23244      * <p>
23245      * <em>If using paging, this may not be the total size of the dataset. If the data object
23246      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23247      * the data set size</em>
23248      */
23249     getCount : function(){
23250         return this.data.length || 0;
23251     },
23252
23253     /**
23254      * Gets the total number of records in the dataset as returned by the server.
23255      * <p>
23256      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23257      * the dataset size</em>
23258      */
23259     getTotalCount : function(){
23260         return this.totalLength || 0;
23261     },
23262
23263     /**
23264      * Returns the sort state of the Store as an object with two properties:
23265      * <pre><code>
23266  field {String} The name of the field by which the Records are sorted
23267  direction {String} The sort order, "ASC" or "DESC"
23268      * </code></pre>
23269      */
23270     getSortState : function(){
23271         return this.sortInfo;
23272     },
23273
23274     // private
23275     applySort : function(){
23276         if(this.sortInfo && !this.remoteSort){
23277             var s = this.sortInfo, f = s.field;
23278             var st = this.fields.get(f).sortType;
23279             var fn = function(r1, r2){
23280                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23281                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23282             };
23283             this.data.sort(s.direction, fn);
23284             if(this.snapshot && this.snapshot != this.data){
23285                 this.snapshot.sort(s.direction, fn);
23286             }
23287         }
23288     },
23289
23290     /**
23291      * Sets the default sort column and order to be used by the next load operation.
23292      * @param {String} fieldName The name of the field to sort by.
23293      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23294      */
23295     setDefaultSort : function(field, dir){
23296         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23297     },
23298
23299     /**
23300      * Sort the Records.
23301      * If remote sorting is used, the sort is performed on the server, and the cache is
23302      * reloaded. If local sorting is used, the cache is sorted internally.
23303      * @param {String} fieldName The name of the field to sort by.
23304      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23305      */
23306     sort : function(fieldName, dir){
23307         var f = this.fields.get(fieldName);
23308         if(!dir){
23309             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23310             
23311             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23312                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23313             }else{
23314                 dir = f.sortDir;
23315             }
23316         }
23317         this.sortToggle[f.name] = dir;
23318         this.sortInfo = {field: f.name, direction: dir};
23319         if(!this.remoteSort){
23320             this.applySort();
23321             this.fireEvent("datachanged", this);
23322         }else{
23323             this.load(this.lastOptions);
23324         }
23325     },
23326
23327     /**
23328      * Calls the specified function for each of the Records in the cache.
23329      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23330      * Returning <em>false</em> aborts and exits the iteration.
23331      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23332      */
23333     each : function(fn, scope){
23334         this.data.each(fn, scope);
23335     },
23336
23337     /**
23338      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23339      * (e.g., during paging).
23340      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23341      */
23342     getModifiedRecords : function(){
23343         return this.modified;
23344     },
23345
23346     // private
23347     createFilterFn : function(property, value, anyMatch){
23348         if(!value.exec){ // not a regex
23349             value = String(value);
23350             if(value.length == 0){
23351                 return false;
23352             }
23353             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23354         }
23355         return function(r){
23356             return value.test(r.data[property]);
23357         };
23358     },
23359
23360     /**
23361      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23362      * @param {String} property A field on your records
23363      * @param {Number} start The record index to start at (defaults to 0)
23364      * @param {Number} end The last record index to include (defaults to length - 1)
23365      * @return {Number} The sum
23366      */
23367     sum : function(property, start, end){
23368         var rs = this.data.items, v = 0;
23369         start = start || 0;
23370         end = (end || end === 0) ? end : rs.length-1;
23371
23372         for(var i = start; i <= end; i++){
23373             v += (rs[i].data[property] || 0);
23374         }
23375         return v;
23376     },
23377
23378     /**
23379      * Filter the records by a specified property.
23380      * @param {String} field A field on your records
23381      * @param {String/RegExp} value Either a string that the field
23382      * should start with or a RegExp to test against the field
23383      * @param {Boolean} anyMatch True to match any part not just the beginning
23384      */
23385     filter : function(property, value, anyMatch){
23386         var fn = this.createFilterFn(property, value, anyMatch);
23387         return fn ? this.filterBy(fn) : this.clearFilter();
23388     },
23389
23390     /**
23391      * Filter by a function. The specified function will be called with each
23392      * record in this data source. If the function returns true the record is included,
23393      * otherwise it is filtered.
23394      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23395      * @param {Object} scope (optional) The scope of the function (defaults to this)
23396      */
23397     filterBy : function(fn, scope){
23398         this.snapshot = this.snapshot || this.data;
23399         this.data = this.queryBy(fn, scope||this);
23400         this.fireEvent("datachanged", this);
23401     },
23402
23403     /**
23404      * Query the records by a specified property.
23405      * @param {String} field A field on your records
23406      * @param {String/RegExp} value Either a string that the field
23407      * should start with or a RegExp to test against the field
23408      * @param {Boolean} anyMatch True to match any part not just the beginning
23409      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23410      */
23411     query : function(property, value, anyMatch){
23412         var fn = this.createFilterFn(property, value, anyMatch);
23413         return fn ? this.queryBy(fn) : this.data.clone();
23414     },
23415
23416     /**
23417      * Query by a function. The specified function will be called with each
23418      * record in this data source. If the function returns true the record is included
23419      * in the results.
23420      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23421      * @param {Object} scope (optional) The scope of the function (defaults to this)
23422       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23423      **/
23424     queryBy : function(fn, scope){
23425         var data = this.snapshot || this.data;
23426         return data.filterBy(fn, scope||this);
23427     },
23428
23429     /**
23430      * Collects unique values for a particular dataIndex from this store.
23431      * @param {String} dataIndex The property to collect
23432      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23433      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23434      * @return {Array} An array of the unique values
23435      **/
23436     collect : function(dataIndex, allowNull, bypassFilter){
23437         var d = (bypassFilter === true && this.snapshot) ?
23438                 this.snapshot.items : this.data.items;
23439         var v, sv, r = [], l = {};
23440         for(var i = 0, len = d.length; i < len; i++){
23441             v = d[i].data[dataIndex];
23442             sv = String(v);
23443             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23444                 l[sv] = true;
23445                 r[r.length] = v;
23446             }
23447         }
23448         return r;
23449     },
23450
23451     /**
23452      * Revert to a view of the Record cache with no filtering applied.
23453      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23454      */
23455     clearFilter : function(suppressEvent){
23456         if(this.snapshot && this.snapshot != this.data){
23457             this.data = this.snapshot;
23458             delete this.snapshot;
23459             if(suppressEvent !== true){
23460                 this.fireEvent("datachanged", this);
23461             }
23462         }
23463     },
23464
23465     // private
23466     afterEdit : function(record){
23467         if(this.modified.indexOf(record) == -1){
23468             this.modified.push(record);
23469         }
23470         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23471     },
23472     
23473     // private
23474     afterReject : function(record){
23475         this.modified.remove(record);
23476         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23477     },
23478
23479     // private
23480     afterCommit : function(record){
23481         this.modified.remove(record);
23482         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23483     },
23484
23485     /**
23486      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23487      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23488      */
23489     commitChanges : function(){
23490         var m = this.modified.slice(0);
23491         this.modified = [];
23492         for(var i = 0, len = m.length; i < len; i++){
23493             m[i].commit();
23494         }
23495     },
23496
23497     /**
23498      * Cancel outstanding changes on all changed records.
23499      */
23500     rejectChanges : function(){
23501         var m = this.modified.slice(0);
23502         this.modified = [];
23503         for(var i = 0, len = m.length; i < len; i++){
23504             m[i].reject();
23505         }
23506     },
23507
23508     onMetaChange : function(meta, rtype, o){
23509         this.recordType = rtype;
23510         this.fields = rtype.prototype.fields;
23511         delete this.snapshot;
23512         this.sortInfo = meta.sortInfo || this.sortInfo;
23513         this.modified = [];
23514         this.fireEvent('metachange', this, this.reader.meta);
23515     },
23516     
23517     moveIndex : function(data, type)
23518     {
23519         var index = this.indexOf(data);
23520         
23521         var newIndex = index + type;
23522         
23523         this.remove(data);
23524         
23525         this.insert(newIndex, data);
23526         
23527     }
23528 });/*
23529  * Based on:
23530  * Ext JS Library 1.1.1
23531  * Copyright(c) 2006-2007, Ext JS, LLC.
23532  *
23533  * Originally Released Under LGPL - original licence link has changed is not relivant.
23534  *
23535  * Fork - LGPL
23536  * <script type="text/javascript">
23537  */
23538
23539 /**
23540  * @class Roo.data.SimpleStore
23541  * @extends Roo.data.Store
23542  * Small helper class to make creating Stores from Array data easier.
23543  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23544  * @cfg {Array} fields An array of field definition objects, or field name strings.
23545  * @cfg {Array} data The multi-dimensional array of data
23546  * @constructor
23547  * @param {Object} config
23548  */
23549 Roo.data.SimpleStore = function(config){
23550     Roo.data.SimpleStore.superclass.constructor.call(this, {
23551         isLocal : true,
23552         reader: new Roo.data.ArrayReader({
23553                 id: config.id
23554             },
23555             Roo.data.Record.create(config.fields)
23556         ),
23557         proxy : new Roo.data.MemoryProxy(config.data)
23558     });
23559     this.load();
23560 };
23561 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23562  * Based on:
23563  * Ext JS Library 1.1.1
23564  * Copyright(c) 2006-2007, Ext JS, LLC.
23565  *
23566  * Originally Released Under LGPL - original licence link has changed is not relivant.
23567  *
23568  * Fork - LGPL
23569  * <script type="text/javascript">
23570  */
23571
23572 /**
23573 /**
23574  * @extends Roo.data.Store
23575  * @class Roo.data.JsonStore
23576  * Small helper class to make creating Stores for JSON data easier. <br/>
23577 <pre><code>
23578 var store = new Roo.data.JsonStore({
23579     url: 'get-images.php',
23580     root: 'images',
23581     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23582 });
23583 </code></pre>
23584  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23585  * JsonReader and HttpProxy (unless inline data is provided).</b>
23586  * @cfg {Array} fields An array of field definition objects, or field name strings.
23587  * @constructor
23588  * @param {Object} config
23589  */
23590 Roo.data.JsonStore = function(c){
23591     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23592         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23593         reader: new Roo.data.JsonReader(c, c.fields)
23594     }));
23595 };
23596 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23597  * Based on:
23598  * Ext JS Library 1.1.1
23599  * Copyright(c) 2006-2007, Ext JS, LLC.
23600  *
23601  * Originally Released Under LGPL - original licence link has changed is not relivant.
23602  *
23603  * Fork - LGPL
23604  * <script type="text/javascript">
23605  */
23606
23607  
23608 Roo.data.Field = function(config){
23609     if(typeof config == "string"){
23610         config = {name: config};
23611     }
23612     Roo.apply(this, config);
23613     
23614     if(!this.type){
23615         this.type = "auto";
23616     }
23617     
23618     var st = Roo.data.SortTypes;
23619     // named sortTypes are supported, here we look them up
23620     if(typeof this.sortType == "string"){
23621         this.sortType = st[this.sortType];
23622     }
23623     
23624     // set default sortType for strings and dates
23625     if(!this.sortType){
23626         switch(this.type){
23627             case "string":
23628                 this.sortType = st.asUCString;
23629                 break;
23630             case "date":
23631                 this.sortType = st.asDate;
23632                 break;
23633             default:
23634                 this.sortType = st.none;
23635         }
23636     }
23637
23638     // define once
23639     var stripRe = /[\$,%]/g;
23640
23641     // prebuilt conversion function for this field, instead of
23642     // switching every time we're reading a value
23643     if(!this.convert){
23644         var cv, dateFormat = this.dateFormat;
23645         switch(this.type){
23646             case "":
23647             case "auto":
23648             case undefined:
23649                 cv = function(v){ return v; };
23650                 break;
23651             case "string":
23652                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23653                 break;
23654             case "int":
23655                 cv = function(v){
23656                     return v !== undefined && v !== null && v !== '' ?
23657                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23658                     };
23659                 break;
23660             case "float":
23661                 cv = function(v){
23662                     return v !== undefined && v !== null && v !== '' ?
23663                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23664                     };
23665                 break;
23666             case "bool":
23667             case "boolean":
23668                 cv = function(v){ return v === true || v === "true" || v == 1; };
23669                 break;
23670             case "date":
23671                 cv = function(v){
23672                     if(!v){
23673                         return '';
23674                     }
23675                     if(v instanceof Date){
23676                         return v;
23677                     }
23678                     if(dateFormat){
23679                         if(dateFormat == "timestamp"){
23680                             return new Date(v*1000);
23681                         }
23682                         return Date.parseDate(v, dateFormat);
23683                     }
23684                     var parsed = Date.parse(v);
23685                     return parsed ? new Date(parsed) : null;
23686                 };
23687              break;
23688             
23689         }
23690         this.convert = cv;
23691     }
23692 };
23693
23694 Roo.data.Field.prototype = {
23695     dateFormat: null,
23696     defaultValue: "",
23697     mapping: null,
23698     sortType : null,
23699     sortDir : "ASC"
23700 };/*
23701  * Based on:
23702  * Ext JS Library 1.1.1
23703  * Copyright(c) 2006-2007, Ext JS, LLC.
23704  *
23705  * Originally Released Under LGPL - original licence link has changed is not relivant.
23706  *
23707  * Fork - LGPL
23708  * <script type="text/javascript">
23709  */
23710  
23711 // Base class for reading structured data from a data source.  This class is intended to be
23712 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23713
23714 /**
23715  * @class Roo.data.DataReader
23716  * Base class for reading structured data from a data source.  This class is intended to be
23717  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23718  */
23719
23720 Roo.data.DataReader = function(meta, recordType){
23721     
23722     this.meta = meta;
23723     
23724     this.recordType = recordType instanceof Array ? 
23725         Roo.data.Record.create(recordType) : recordType;
23726 };
23727
23728 Roo.data.DataReader.prototype = {
23729      /**
23730      * Create an empty record
23731      * @param {Object} data (optional) - overlay some values
23732      * @return {Roo.data.Record} record created.
23733      */
23734     newRow :  function(d) {
23735         var da =  {};
23736         this.recordType.prototype.fields.each(function(c) {
23737             switch( c.type) {
23738                 case 'int' : da[c.name] = 0; break;
23739                 case 'date' : da[c.name] = new Date(); break;
23740                 case 'float' : da[c.name] = 0.0; break;
23741                 case 'boolean' : da[c.name] = false; break;
23742                 default : da[c.name] = ""; break;
23743             }
23744             
23745         });
23746         return new this.recordType(Roo.apply(da, d));
23747     }
23748     
23749 };/*
23750  * Based on:
23751  * Ext JS Library 1.1.1
23752  * Copyright(c) 2006-2007, Ext JS, LLC.
23753  *
23754  * Originally Released Under LGPL - original licence link has changed is not relivant.
23755  *
23756  * Fork - LGPL
23757  * <script type="text/javascript">
23758  */
23759
23760 /**
23761  * @class Roo.data.DataProxy
23762  * @extends Roo.data.Observable
23763  * This class is an abstract base class for implementations which provide retrieval of
23764  * unformatted data objects.<br>
23765  * <p>
23766  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23767  * (of the appropriate type which knows how to parse the data object) to provide a block of
23768  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23769  * <p>
23770  * Custom implementations must implement the load method as described in
23771  * {@link Roo.data.HttpProxy#load}.
23772  */
23773 Roo.data.DataProxy = function(){
23774     this.addEvents({
23775         /**
23776          * @event beforeload
23777          * Fires before a network request is made to retrieve a data object.
23778          * @param {Object} This DataProxy object.
23779          * @param {Object} params The params parameter to the load function.
23780          */
23781         beforeload : true,
23782         /**
23783          * @event load
23784          * Fires before the load method's callback is called.
23785          * @param {Object} This DataProxy object.
23786          * @param {Object} o The data object.
23787          * @param {Object} arg The callback argument object passed to the load function.
23788          */
23789         load : true,
23790         /**
23791          * @event loadexception
23792          * Fires if an Exception occurs during data retrieval.
23793          * @param {Object} This DataProxy object.
23794          * @param {Object} o The data object.
23795          * @param {Object} arg The callback argument object passed to the load function.
23796          * @param {Object} e The Exception.
23797          */
23798         loadexception : true
23799     });
23800     Roo.data.DataProxy.superclass.constructor.call(this);
23801 };
23802
23803 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23804
23805     /**
23806      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23807      */
23808 /*
23809  * Based on:
23810  * Ext JS Library 1.1.1
23811  * Copyright(c) 2006-2007, Ext JS, LLC.
23812  *
23813  * Originally Released Under LGPL - original licence link has changed is not relivant.
23814  *
23815  * Fork - LGPL
23816  * <script type="text/javascript">
23817  */
23818 /**
23819  * @class Roo.data.MemoryProxy
23820  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23821  * to the Reader when its load method is called.
23822  * @constructor
23823  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23824  */
23825 Roo.data.MemoryProxy = function(data){
23826     if (data.data) {
23827         data = data.data;
23828     }
23829     Roo.data.MemoryProxy.superclass.constructor.call(this);
23830     this.data = data;
23831 };
23832
23833 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23834     
23835     /**
23836      * Load data from the requested source (in this case an in-memory
23837      * data object passed to the constructor), read the data object into
23838      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23839      * process that block using the passed callback.
23840      * @param {Object} params This parameter is not used by the MemoryProxy class.
23841      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23842      * object into a block of Roo.data.Records.
23843      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23844      * The function must be passed <ul>
23845      * <li>The Record block object</li>
23846      * <li>The "arg" argument from the load function</li>
23847      * <li>A boolean success indicator</li>
23848      * </ul>
23849      * @param {Object} scope The scope in which to call the callback
23850      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23851      */
23852     load : function(params, reader, callback, scope, arg){
23853         params = params || {};
23854         var result;
23855         try {
23856             result = reader.readRecords(params.data ? params.data :this.data);
23857         }catch(e){
23858             this.fireEvent("loadexception", this, arg, null, e);
23859             callback.call(scope, null, arg, false);
23860             return;
23861         }
23862         callback.call(scope, result, arg, true);
23863     },
23864     
23865     // private
23866     update : function(params, records){
23867         
23868     }
23869 });/*
23870  * Based on:
23871  * Ext JS Library 1.1.1
23872  * Copyright(c) 2006-2007, Ext JS, LLC.
23873  *
23874  * Originally Released Under LGPL - original licence link has changed is not relivant.
23875  *
23876  * Fork - LGPL
23877  * <script type="text/javascript">
23878  */
23879 /**
23880  * @class Roo.data.HttpProxy
23881  * @extends Roo.data.DataProxy
23882  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23883  * configured to reference a certain URL.<br><br>
23884  * <p>
23885  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23886  * from which the running page was served.<br><br>
23887  * <p>
23888  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23889  * <p>
23890  * Be aware that to enable the browser to parse an XML document, the server must set
23891  * the Content-Type header in the HTTP response to "text/xml".
23892  * @constructor
23893  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23894  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23895  * will be used to make the request.
23896  */
23897 Roo.data.HttpProxy = function(conn){
23898     Roo.data.HttpProxy.superclass.constructor.call(this);
23899     // is conn a conn config or a real conn?
23900     this.conn = conn;
23901     this.useAjax = !conn || !conn.events;
23902   
23903 };
23904
23905 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23906     // thse are take from connection...
23907     
23908     /**
23909      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23910      */
23911     /**
23912      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23913      * extra parameters to each request made by this object. (defaults to undefined)
23914      */
23915     /**
23916      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23917      *  to each request made by this object. (defaults to undefined)
23918      */
23919     /**
23920      * @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)
23921      */
23922     /**
23923      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23924      */
23925      /**
23926      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23927      * @type Boolean
23928      */
23929   
23930
23931     /**
23932      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23933      * @type Boolean
23934      */
23935     /**
23936      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23937      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23938      * a finer-grained basis than the DataProxy events.
23939      */
23940     getConnection : function(){
23941         return this.useAjax ? Roo.Ajax : this.conn;
23942     },
23943
23944     /**
23945      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23946      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23947      * process that block using the passed callback.
23948      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23949      * for the request to the remote server.
23950      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23951      * object into a block of Roo.data.Records.
23952      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23953      * The function must be passed <ul>
23954      * <li>The Record block object</li>
23955      * <li>The "arg" argument from the load function</li>
23956      * <li>A boolean success indicator</li>
23957      * </ul>
23958      * @param {Object} scope The scope in which to call the callback
23959      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23960      */
23961     load : function(params, reader, callback, scope, arg){
23962         if(this.fireEvent("beforeload", this, params) !== false){
23963             var  o = {
23964                 params : params || {},
23965                 request: {
23966                     callback : callback,
23967                     scope : scope,
23968                     arg : arg
23969                 },
23970                 reader: reader,
23971                 callback : this.loadResponse,
23972                 scope: this
23973             };
23974             if(this.useAjax){
23975                 Roo.applyIf(o, this.conn);
23976                 if(this.activeRequest){
23977                     Roo.Ajax.abort(this.activeRequest);
23978                 }
23979                 this.activeRequest = Roo.Ajax.request(o);
23980             }else{
23981                 this.conn.request(o);
23982             }
23983         }else{
23984             callback.call(scope||this, null, arg, false);
23985         }
23986     },
23987
23988     // private
23989     loadResponse : function(o, success, response){
23990         delete this.activeRequest;
23991         if(!success){
23992             this.fireEvent("loadexception", this, o, response);
23993             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23994             return;
23995         }
23996         var result;
23997         try {
23998             result = o.reader.read(response);
23999         }catch(e){
24000             this.fireEvent("loadexception", this, o, response, e);
24001             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24002             return;
24003         }
24004         
24005         this.fireEvent("load", this, o, o.request.arg);
24006         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24007     },
24008
24009     // private
24010     update : function(dataSet){
24011
24012     },
24013
24014     // private
24015     updateResponse : function(dataSet){
24016
24017     }
24018 });/*
24019  * Based on:
24020  * Ext JS Library 1.1.1
24021  * Copyright(c) 2006-2007, Ext JS, LLC.
24022  *
24023  * Originally Released Under LGPL - original licence link has changed is not relivant.
24024  *
24025  * Fork - LGPL
24026  * <script type="text/javascript">
24027  */
24028
24029 /**
24030  * @class Roo.data.ScriptTagProxy
24031  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24032  * other than the originating domain of the running page.<br><br>
24033  * <p>
24034  * <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
24035  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24036  * <p>
24037  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24038  * source code that is used as the source inside a &lt;script> tag.<br><br>
24039  * <p>
24040  * In order for the browser to process the returned data, the server must wrap the data object
24041  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24042  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24043  * depending on whether the callback name was passed:
24044  * <p>
24045  * <pre><code>
24046 boolean scriptTag = false;
24047 String cb = request.getParameter("callback");
24048 if (cb != null) {
24049     scriptTag = true;
24050     response.setContentType("text/javascript");
24051 } else {
24052     response.setContentType("application/x-json");
24053 }
24054 Writer out = response.getWriter();
24055 if (scriptTag) {
24056     out.write(cb + "(");
24057 }
24058 out.print(dataBlock.toJsonString());
24059 if (scriptTag) {
24060     out.write(");");
24061 }
24062 </pre></code>
24063  *
24064  * @constructor
24065  * @param {Object} config A configuration object.
24066  */
24067 Roo.data.ScriptTagProxy = function(config){
24068     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24069     Roo.apply(this, config);
24070     this.head = document.getElementsByTagName("head")[0];
24071 };
24072
24073 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24074
24075 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24076     /**
24077      * @cfg {String} url The URL from which to request the data object.
24078      */
24079     /**
24080      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24081      */
24082     timeout : 30000,
24083     /**
24084      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24085      * the server the name of the callback function set up by the load call to process the returned data object.
24086      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24087      * javascript output which calls this named function passing the data object as its only parameter.
24088      */
24089     callbackParam : "callback",
24090     /**
24091      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24092      * name to the request.
24093      */
24094     nocache : true,
24095
24096     /**
24097      * Load data from the configured URL, read the data object into
24098      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24099      * process that block using the passed callback.
24100      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24101      * for the request to the remote server.
24102      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24103      * object into a block of Roo.data.Records.
24104      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24105      * The function must be passed <ul>
24106      * <li>The Record block object</li>
24107      * <li>The "arg" argument from the load function</li>
24108      * <li>A boolean success indicator</li>
24109      * </ul>
24110      * @param {Object} scope The scope in which to call the callback
24111      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24112      */
24113     load : function(params, reader, callback, scope, arg){
24114         if(this.fireEvent("beforeload", this, params) !== false){
24115
24116             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24117
24118             var url = this.url;
24119             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24120             if(this.nocache){
24121                 url += "&_dc=" + (new Date().getTime());
24122             }
24123             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24124             var trans = {
24125                 id : transId,
24126                 cb : "stcCallback"+transId,
24127                 scriptId : "stcScript"+transId,
24128                 params : params,
24129                 arg : arg,
24130                 url : url,
24131                 callback : callback,
24132                 scope : scope,
24133                 reader : reader
24134             };
24135             var conn = this;
24136
24137             window[trans.cb] = function(o){
24138                 conn.handleResponse(o, trans);
24139             };
24140
24141             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24142
24143             if(this.autoAbort !== false){
24144                 this.abort();
24145             }
24146
24147             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24148
24149             var script = document.createElement("script");
24150             script.setAttribute("src", url);
24151             script.setAttribute("type", "text/javascript");
24152             script.setAttribute("id", trans.scriptId);
24153             this.head.appendChild(script);
24154
24155             this.trans = trans;
24156         }else{
24157             callback.call(scope||this, null, arg, false);
24158         }
24159     },
24160
24161     // private
24162     isLoading : function(){
24163         return this.trans ? true : false;
24164     },
24165
24166     /**
24167      * Abort the current server request.
24168      */
24169     abort : function(){
24170         if(this.isLoading()){
24171             this.destroyTrans(this.trans);
24172         }
24173     },
24174
24175     // private
24176     destroyTrans : function(trans, isLoaded){
24177         this.head.removeChild(document.getElementById(trans.scriptId));
24178         clearTimeout(trans.timeoutId);
24179         if(isLoaded){
24180             window[trans.cb] = undefined;
24181             try{
24182                 delete window[trans.cb];
24183             }catch(e){}
24184         }else{
24185             // if hasn't been loaded, wait for load to remove it to prevent script error
24186             window[trans.cb] = function(){
24187                 window[trans.cb] = undefined;
24188                 try{
24189                     delete window[trans.cb];
24190                 }catch(e){}
24191             };
24192         }
24193     },
24194
24195     // private
24196     handleResponse : function(o, trans){
24197         this.trans = false;
24198         this.destroyTrans(trans, true);
24199         var result;
24200         try {
24201             result = trans.reader.readRecords(o);
24202         }catch(e){
24203             this.fireEvent("loadexception", this, o, trans.arg, e);
24204             trans.callback.call(trans.scope||window, null, trans.arg, false);
24205             return;
24206         }
24207         this.fireEvent("load", this, o, trans.arg);
24208         trans.callback.call(trans.scope||window, result, trans.arg, true);
24209     },
24210
24211     // private
24212     handleFailure : function(trans){
24213         this.trans = false;
24214         this.destroyTrans(trans, false);
24215         this.fireEvent("loadexception", this, null, trans.arg);
24216         trans.callback.call(trans.scope||window, null, trans.arg, false);
24217     }
24218 });/*
24219  * Based on:
24220  * Ext JS Library 1.1.1
24221  * Copyright(c) 2006-2007, Ext JS, LLC.
24222  *
24223  * Originally Released Under LGPL - original licence link has changed is not relivant.
24224  *
24225  * Fork - LGPL
24226  * <script type="text/javascript">
24227  */
24228
24229 /**
24230  * @class Roo.data.JsonReader
24231  * @extends Roo.data.DataReader
24232  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24233  * based on mappings in a provided Roo.data.Record constructor.
24234  * 
24235  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24236  * in the reply previously. 
24237  * 
24238  * <p>
24239  * Example code:
24240  * <pre><code>
24241 var RecordDef = Roo.data.Record.create([
24242     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24243     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24244 ]);
24245 var myReader = new Roo.data.JsonReader({
24246     totalProperty: "results",    // The property which contains the total dataset size (optional)
24247     root: "rows",                // The property which contains an Array of row objects
24248     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24249 }, RecordDef);
24250 </code></pre>
24251  * <p>
24252  * This would consume a JSON file like this:
24253  * <pre><code>
24254 { 'results': 2, 'rows': [
24255     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24256     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24257 }
24258 </code></pre>
24259  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24260  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24261  * paged from the remote server.
24262  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24263  * @cfg {String} root name of the property which contains the Array of row objects.
24264  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24265  * @cfg {Array} fields Array of field definition objects
24266  * @constructor
24267  * Create a new JsonReader
24268  * @param {Object} meta Metadata configuration options
24269  * @param {Object} recordType Either an Array of field definition objects,
24270  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24271  */
24272 Roo.data.JsonReader = function(meta, recordType){
24273     
24274     meta = meta || {};
24275     // set some defaults:
24276     Roo.applyIf(meta, {
24277         totalProperty: 'total',
24278         successProperty : 'success',
24279         root : 'data',
24280         id : 'id'
24281     });
24282     
24283     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24284 };
24285 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24286     
24287     /**
24288      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24289      * Used by Store query builder to append _requestMeta to params.
24290      * 
24291      */
24292     metaFromRemote : false,
24293     /**
24294      * This method is only used by a DataProxy which has retrieved data from a remote server.
24295      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24296      * @return {Object} data A data block which is used by an Roo.data.Store object as
24297      * a cache of Roo.data.Records.
24298      */
24299     read : function(response){
24300         var json = response.responseText;
24301        
24302         var o = /* eval:var:o */ eval("("+json+")");
24303         if(!o) {
24304             throw {message: "JsonReader.read: Json object not found"};
24305         }
24306         
24307         if(o.metaData){
24308             
24309             delete this.ef;
24310             this.metaFromRemote = true;
24311             this.meta = o.metaData;
24312             this.recordType = Roo.data.Record.create(o.metaData.fields);
24313             this.onMetaChange(this.meta, this.recordType, o);
24314         }
24315         return this.readRecords(o);
24316     },
24317
24318     // private function a store will implement
24319     onMetaChange : function(meta, recordType, o){
24320
24321     },
24322
24323     /**
24324          * @ignore
24325          */
24326     simpleAccess: function(obj, subsc) {
24327         return obj[subsc];
24328     },
24329
24330         /**
24331          * @ignore
24332          */
24333     getJsonAccessor: function(){
24334         var re = /[\[\.]/;
24335         return function(expr) {
24336             try {
24337                 return(re.test(expr))
24338                     ? new Function("obj", "return obj." + expr)
24339                     : function(obj){
24340                         return obj[expr];
24341                     };
24342             } catch(e){}
24343             return Roo.emptyFn;
24344         };
24345     }(),
24346
24347     /**
24348      * Create a data block containing Roo.data.Records from an XML document.
24349      * @param {Object} o An object which contains an Array of row objects in the property specified
24350      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24351      * which contains the total size of the dataset.
24352      * @return {Object} data A data block which is used by an Roo.data.Store object as
24353      * a cache of Roo.data.Records.
24354      */
24355     readRecords : function(o){
24356         /**
24357          * After any data loads, the raw JSON data is available for further custom processing.
24358          * @type Object
24359          */
24360         this.o = o;
24361         var s = this.meta, Record = this.recordType,
24362             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24363
24364 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24365         if (!this.ef) {
24366             if(s.totalProperty) {
24367                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24368                 }
24369                 if(s.successProperty) {
24370                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24371                 }
24372                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24373                 if (s.id) {
24374                         var g = this.getJsonAccessor(s.id);
24375                         this.getId = function(rec) {
24376                                 var r = g(rec);  
24377                                 return (r === undefined || r === "") ? null : r;
24378                         };
24379                 } else {
24380                         this.getId = function(){return null;};
24381                 }
24382             this.ef = [];
24383             for(var jj = 0; jj < fl; jj++){
24384                 f = fi[jj];
24385                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24386                 this.ef[jj] = this.getJsonAccessor(map);
24387             }
24388         }
24389
24390         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24391         if(s.totalProperty){
24392             var vt = parseInt(this.getTotal(o), 10);
24393             if(!isNaN(vt)){
24394                 totalRecords = vt;
24395             }
24396         }
24397         if(s.successProperty){
24398             var vs = this.getSuccess(o);
24399             if(vs === false || vs === 'false'){
24400                 success = false;
24401             }
24402         }
24403         var records = [];
24404         for(var i = 0; i < c; i++){
24405                 var n = root[i];
24406             var values = {};
24407             var id = this.getId(n);
24408             for(var j = 0; j < fl; j++){
24409                 f = fi[j];
24410             var v = this.ef[j](n);
24411             if (!f.convert) {
24412                 Roo.log('missing convert for ' + f.name);
24413                 Roo.log(f);
24414                 continue;
24415             }
24416             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24417             }
24418             var record = new Record(values, id);
24419             record.json = n;
24420             records[i] = record;
24421         }
24422         return {
24423             raw : o,
24424             success : success,
24425             records : records,
24426             totalRecords : totalRecords
24427         };
24428     }
24429 });/*
24430  * Based on:
24431  * Ext JS Library 1.1.1
24432  * Copyright(c) 2006-2007, Ext JS, LLC.
24433  *
24434  * Originally Released Under LGPL - original licence link has changed is not relivant.
24435  *
24436  * Fork - LGPL
24437  * <script type="text/javascript">
24438  */
24439
24440 /**
24441  * @class Roo.data.XmlReader
24442  * @extends Roo.data.DataReader
24443  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24444  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24445  * <p>
24446  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24447  * header in the HTTP response must be set to "text/xml".</em>
24448  * <p>
24449  * Example code:
24450  * <pre><code>
24451 var RecordDef = Roo.data.Record.create([
24452    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24453    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24454 ]);
24455 var myReader = new Roo.data.XmlReader({
24456    totalRecords: "results", // The element which contains the total dataset size (optional)
24457    record: "row",           // The repeated element which contains row information
24458    id: "id"                 // The element within the row that provides an ID for the record (optional)
24459 }, RecordDef);
24460 </code></pre>
24461  * <p>
24462  * This would consume an XML file like this:
24463  * <pre><code>
24464 &lt;?xml?>
24465 &lt;dataset>
24466  &lt;results>2&lt;/results>
24467  &lt;row>
24468    &lt;id>1&lt;/id>
24469    &lt;name>Bill&lt;/name>
24470    &lt;occupation>Gardener&lt;/occupation>
24471  &lt;/row>
24472  &lt;row>
24473    &lt;id>2&lt;/id>
24474    &lt;name>Ben&lt;/name>
24475    &lt;occupation>Horticulturalist&lt;/occupation>
24476  &lt;/row>
24477 &lt;/dataset>
24478 </code></pre>
24479  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24480  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24481  * paged from the remote server.
24482  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24483  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24484  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24485  * a record identifier value.
24486  * @constructor
24487  * Create a new XmlReader
24488  * @param {Object} meta Metadata configuration options
24489  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24490  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24491  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24492  */
24493 Roo.data.XmlReader = function(meta, recordType){
24494     meta = meta || {};
24495     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24496 };
24497 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24498     /**
24499      * This method is only used by a DataProxy which has retrieved data from a remote server.
24500          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24501          * to contain a method called 'responseXML' that returns an XML document object.
24502      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24503      * a cache of Roo.data.Records.
24504      */
24505     read : function(response){
24506         var doc = response.responseXML;
24507         if(!doc) {
24508             throw {message: "XmlReader.read: XML Document not available"};
24509         }
24510         return this.readRecords(doc);
24511     },
24512
24513     /**
24514      * Create a data block containing Roo.data.Records from an XML document.
24515          * @param {Object} doc A parsed XML document.
24516      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24517      * a cache of Roo.data.Records.
24518      */
24519     readRecords : function(doc){
24520         /**
24521          * After any data loads/reads, the raw XML Document is available for further custom processing.
24522          * @type XMLDocument
24523          */
24524         this.xmlData = doc;
24525         var root = doc.documentElement || doc;
24526         var q = Roo.DomQuery;
24527         var recordType = this.recordType, fields = recordType.prototype.fields;
24528         var sid = this.meta.id;
24529         var totalRecords = 0, success = true;
24530         if(this.meta.totalRecords){
24531             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24532         }
24533         
24534         if(this.meta.success){
24535             var sv = q.selectValue(this.meta.success, root, true);
24536             success = sv !== false && sv !== 'false';
24537         }
24538         var records = [];
24539         var ns = q.select(this.meta.record, root);
24540         for(var i = 0, len = ns.length; i < len; i++) {
24541                 var n = ns[i];
24542                 var values = {};
24543                 var id = sid ? q.selectValue(sid, n) : undefined;
24544                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24545                     var f = fields.items[j];
24546                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24547                     v = f.convert(v);
24548                     values[f.name] = v;
24549                 }
24550                 var record = new recordType(values, id);
24551                 record.node = n;
24552                 records[records.length] = record;
24553             }
24554
24555             return {
24556                 success : success,
24557                 records : records,
24558                 totalRecords : totalRecords || records.length
24559             };
24560     }
24561 });/*
24562  * Based on:
24563  * Ext JS Library 1.1.1
24564  * Copyright(c) 2006-2007, Ext JS, LLC.
24565  *
24566  * Originally Released Under LGPL - original licence link has changed is not relivant.
24567  *
24568  * Fork - LGPL
24569  * <script type="text/javascript">
24570  */
24571
24572 /**
24573  * @class Roo.data.ArrayReader
24574  * @extends Roo.data.DataReader
24575  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24576  * Each element of that Array represents a row of data fields. The
24577  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24578  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24579  * <p>
24580  * Example code:.
24581  * <pre><code>
24582 var RecordDef = Roo.data.Record.create([
24583     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24584     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24585 ]);
24586 var myReader = new Roo.data.ArrayReader({
24587     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24588 }, RecordDef);
24589 </code></pre>
24590  * <p>
24591  * This would consume an Array like this:
24592  * <pre><code>
24593 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24594   </code></pre>
24595  
24596  * @constructor
24597  * Create a new JsonReader
24598  * @param {Object} meta Metadata configuration options.
24599  * @param {Object|Array} recordType Either an Array of field definition objects
24600  * 
24601  * @cfg {Array} fields Array of field definition objects
24602  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24603  * as specified to {@link Roo.data.Record#create},
24604  * or an {@link Roo.data.Record} object
24605  *
24606  * 
24607  * created using {@link Roo.data.Record#create}.
24608  */
24609 Roo.data.ArrayReader = function(meta, recordType){
24610     
24611      
24612     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24613 };
24614
24615 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24616     /**
24617      * Create a data block containing Roo.data.Records from an XML document.
24618      * @param {Object} o An Array of row objects which represents the dataset.
24619      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
24620      * a cache of Roo.data.Records.
24621      */
24622     readRecords : function(o)
24623     {
24624         var sid = this.meta ? this.meta.id : null;
24625         var recordType = this.recordType, fields = recordType.prototype.fields;
24626         var records = [];
24627         var root = o;
24628         for(var i = 0; i < root.length; i++){
24629                 var n = root[i];
24630             var values = {};
24631             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24632             for(var j = 0, jlen = fields.length; j < jlen; j++){
24633                 var f = fields.items[j];
24634                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24635                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24636                 v = f.convert(v);
24637                 values[f.name] = v;
24638             }
24639             var record = new recordType(values, id);
24640             record.json = n;
24641             records[records.length] = record;
24642         }
24643         return {
24644             records : records,
24645             totalRecords : records.length
24646         };
24647     }
24648 });/*
24649  * Based on:
24650  * Ext JS Library 1.1.1
24651  * Copyright(c) 2006-2007, Ext JS, LLC.
24652  *
24653  * Originally Released Under LGPL - original licence link has changed is not relivant.
24654  *
24655  * Fork - LGPL
24656  * <script type="text/javascript">
24657  */
24658
24659
24660 /**
24661  * @class Roo.data.Tree
24662  * @extends Roo.util.Observable
24663  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24664  * in the tree have most standard DOM functionality.
24665  * @constructor
24666  * @param {Node} root (optional) The root node
24667  */
24668 Roo.data.Tree = function(root){
24669    this.nodeHash = {};
24670    /**
24671     * The root node for this tree
24672     * @type Node
24673     */
24674    this.root = null;
24675    if(root){
24676        this.setRootNode(root);
24677    }
24678    this.addEvents({
24679        /**
24680         * @event append
24681         * Fires when a new child node is appended to a node in this tree.
24682         * @param {Tree} tree The owner tree
24683         * @param {Node} parent The parent node
24684         * @param {Node} node The newly appended node
24685         * @param {Number} index The index of the newly appended node
24686         */
24687        "append" : true,
24688        /**
24689         * @event remove
24690         * Fires when a child node is removed from a node in this tree.
24691         * @param {Tree} tree The owner tree
24692         * @param {Node} parent The parent node
24693         * @param {Node} node The child node removed
24694         */
24695        "remove" : true,
24696        /**
24697         * @event move
24698         * Fires when a node is moved to a new location in the tree
24699         * @param {Tree} tree The owner tree
24700         * @param {Node} node The node moved
24701         * @param {Node} oldParent The old parent of this node
24702         * @param {Node} newParent The new parent of this node
24703         * @param {Number} index The index it was moved to
24704         */
24705        "move" : true,
24706        /**
24707         * @event insert
24708         * Fires when a new child node is inserted in a node in this tree.
24709         * @param {Tree} tree The owner tree
24710         * @param {Node} parent The parent node
24711         * @param {Node} node The child node inserted
24712         * @param {Node} refNode The child node the node was inserted before
24713         */
24714        "insert" : true,
24715        /**
24716         * @event beforeappend
24717         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24718         * @param {Tree} tree The owner tree
24719         * @param {Node} parent The parent node
24720         * @param {Node} node The child node to be appended
24721         */
24722        "beforeappend" : true,
24723        /**
24724         * @event beforeremove
24725         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24726         * @param {Tree} tree The owner tree
24727         * @param {Node} parent The parent node
24728         * @param {Node} node The child node to be removed
24729         */
24730        "beforeremove" : true,
24731        /**
24732         * @event beforemove
24733         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24734         * @param {Tree} tree The owner tree
24735         * @param {Node} node The node being moved
24736         * @param {Node} oldParent The parent of the node
24737         * @param {Node} newParent The new parent the node is moving to
24738         * @param {Number} index The index it is being moved to
24739         */
24740        "beforemove" : true,
24741        /**
24742         * @event beforeinsert
24743         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24744         * @param {Tree} tree The owner tree
24745         * @param {Node} parent The parent node
24746         * @param {Node} node The child node to be inserted
24747         * @param {Node} refNode The child node the node is being inserted before
24748         */
24749        "beforeinsert" : true
24750    });
24751
24752     Roo.data.Tree.superclass.constructor.call(this);
24753 };
24754
24755 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24756     pathSeparator: "/",
24757
24758     proxyNodeEvent : function(){
24759         return this.fireEvent.apply(this, arguments);
24760     },
24761
24762     /**
24763      * Returns the root node for this tree.
24764      * @return {Node}
24765      */
24766     getRootNode : function(){
24767         return this.root;
24768     },
24769
24770     /**
24771      * Sets the root node for this tree.
24772      * @param {Node} node
24773      * @return {Node}
24774      */
24775     setRootNode : function(node){
24776         this.root = node;
24777         node.ownerTree = this;
24778         node.isRoot = true;
24779         this.registerNode(node);
24780         return node;
24781     },
24782
24783     /**
24784      * Gets a node in this tree by its id.
24785      * @param {String} id
24786      * @return {Node}
24787      */
24788     getNodeById : function(id){
24789         return this.nodeHash[id];
24790     },
24791
24792     registerNode : function(node){
24793         this.nodeHash[node.id] = node;
24794     },
24795
24796     unregisterNode : function(node){
24797         delete this.nodeHash[node.id];
24798     },
24799
24800     toString : function(){
24801         return "[Tree"+(this.id?" "+this.id:"")+"]";
24802     }
24803 });
24804
24805 /**
24806  * @class Roo.data.Node
24807  * @extends Roo.util.Observable
24808  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24809  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24810  * @constructor
24811  * @param {Object} attributes The attributes/config for the node
24812  */
24813 Roo.data.Node = function(attributes){
24814     /**
24815      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24816      * @type {Object}
24817      */
24818     this.attributes = attributes || {};
24819     this.leaf = this.attributes.leaf;
24820     /**
24821      * The node id. @type String
24822      */
24823     this.id = this.attributes.id;
24824     if(!this.id){
24825         this.id = Roo.id(null, "ynode-");
24826         this.attributes.id = this.id;
24827     }
24828      
24829     
24830     /**
24831      * All child nodes of this node. @type Array
24832      */
24833     this.childNodes = [];
24834     if(!this.childNodes.indexOf){ // indexOf is a must
24835         this.childNodes.indexOf = function(o){
24836             for(var i = 0, len = this.length; i < len; i++){
24837                 if(this[i] == o) {
24838                     return i;
24839                 }
24840             }
24841             return -1;
24842         };
24843     }
24844     /**
24845      * The parent node for this node. @type Node
24846      */
24847     this.parentNode = null;
24848     /**
24849      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24850      */
24851     this.firstChild = null;
24852     /**
24853      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24854      */
24855     this.lastChild = null;
24856     /**
24857      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24858      */
24859     this.previousSibling = null;
24860     /**
24861      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24862      */
24863     this.nextSibling = null;
24864
24865     this.addEvents({
24866        /**
24867         * @event append
24868         * Fires when a new child node is appended
24869         * @param {Tree} tree The owner tree
24870         * @param {Node} this This node
24871         * @param {Node} node The newly appended node
24872         * @param {Number} index The index of the newly appended node
24873         */
24874        "append" : true,
24875        /**
24876         * @event remove
24877         * Fires when a child node is removed
24878         * @param {Tree} tree The owner tree
24879         * @param {Node} this This node
24880         * @param {Node} node The removed node
24881         */
24882        "remove" : true,
24883        /**
24884         * @event move
24885         * Fires when this node is moved to a new location in the tree
24886         * @param {Tree} tree The owner tree
24887         * @param {Node} this This node
24888         * @param {Node} oldParent The old parent of this node
24889         * @param {Node} newParent The new parent of this node
24890         * @param {Number} index The index it was moved to
24891         */
24892        "move" : true,
24893        /**
24894         * @event insert
24895         * Fires when a new child node is inserted.
24896         * @param {Tree} tree The owner tree
24897         * @param {Node} this This node
24898         * @param {Node} node The child node inserted
24899         * @param {Node} refNode The child node the node was inserted before
24900         */
24901        "insert" : true,
24902        /**
24903         * @event beforeappend
24904         * Fires before a new child is appended, return false to cancel the append.
24905         * @param {Tree} tree The owner tree
24906         * @param {Node} this This node
24907         * @param {Node} node The child node to be appended
24908         */
24909        "beforeappend" : true,
24910        /**
24911         * @event beforeremove
24912         * Fires before a child is removed, return false to cancel the remove.
24913         * @param {Tree} tree The owner tree
24914         * @param {Node} this This node
24915         * @param {Node} node The child node to be removed
24916         */
24917        "beforeremove" : true,
24918        /**
24919         * @event beforemove
24920         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24921         * @param {Tree} tree The owner tree
24922         * @param {Node} this This node
24923         * @param {Node} oldParent The parent of this node
24924         * @param {Node} newParent The new parent this node is moving to
24925         * @param {Number} index The index it is being moved to
24926         */
24927        "beforemove" : true,
24928        /**
24929         * @event beforeinsert
24930         * Fires before a new child is inserted, return false to cancel the insert.
24931         * @param {Tree} tree The owner tree
24932         * @param {Node} this This node
24933         * @param {Node} node The child node to be inserted
24934         * @param {Node} refNode The child node the node is being inserted before
24935         */
24936        "beforeinsert" : true
24937    });
24938     this.listeners = this.attributes.listeners;
24939     Roo.data.Node.superclass.constructor.call(this);
24940 };
24941
24942 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24943     fireEvent : function(evtName){
24944         // first do standard event for this node
24945         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24946             return false;
24947         }
24948         // then bubble it up to the tree if the event wasn't cancelled
24949         var ot = this.getOwnerTree();
24950         if(ot){
24951             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24952                 return false;
24953             }
24954         }
24955         return true;
24956     },
24957
24958     /**
24959      * Returns true if this node is a leaf
24960      * @return {Boolean}
24961      */
24962     isLeaf : function(){
24963         return this.leaf === true;
24964     },
24965
24966     // private
24967     setFirstChild : function(node){
24968         this.firstChild = node;
24969     },
24970
24971     //private
24972     setLastChild : function(node){
24973         this.lastChild = node;
24974     },
24975
24976
24977     /**
24978      * Returns true if this node is the last child of its parent
24979      * @return {Boolean}
24980      */
24981     isLast : function(){
24982        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24983     },
24984
24985     /**
24986      * Returns true if this node is the first child of its parent
24987      * @return {Boolean}
24988      */
24989     isFirst : function(){
24990        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24991     },
24992
24993     hasChildNodes : function(){
24994         return !this.isLeaf() && this.childNodes.length > 0;
24995     },
24996
24997     /**
24998      * Insert node(s) as the last child node of this node.
24999      * @param {Node/Array} node The node or Array of nodes to append
25000      * @return {Node} The appended node if single append, or null if an array was passed
25001      */
25002     appendChild : function(node){
25003         var multi = false;
25004         if(node instanceof Array){
25005             multi = node;
25006         }else if(arguments.length > 1){
25007             multi = arguments;
25008         }
25009         
25010         // if passed an array or multiple args do them one by one
25011         if(multi){
25012             for(var i = 0, len = multi.length; i < len; i++) {
25013                 this.appendChild(multi[i]);
25014             }
25015         }else{
25016             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25017                 return false;
25018             }
25019             var index = this.childNodes.length;
25020             var oldParent = node.parentNode;
25021             // it's a move, make sure we move it cleanly
25022             if(oldParent){
25023                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25024                     return false;
25025                 }
25026                 oldParent.removeChild(node);
25027             }
25028             
25029             index = this.childNodes.length;
25030             if(index == 0){
25031                 this.setFirstChild(node);
25032             }
25033             this.childNodes.push(node);
25034             node.parentNode = this;
25035             var ps = this.childNodes[index-1];
25036             if(ps){
25037                 node.previousSibling = ps;
25038                 ps.nextSibling = node;
25039             }else{
25040                 node.previousSibling = null;
25041             }
25042             node.nextSibling = null;
25043             this.setLastChild(node);
25044             node.setOwnerTree(this.getOwnerTree());
25045             this.fireEvent("append", this.ownerTree, this, node, index);
25046             if(this.ownerTree) {
25047                 this.ownerTree.fireEvent("appendnode", this, node, index);
25048             }
25049             if(oldParent){
25050                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25051             }
25052             return node;
25053         }
25054     },
25055
25056     /**
25057      * Removes a child node from this node.
25058      * @param {Node} node The node to remove
25059      * @return {Node} The removed node
25060      */
25061     removeChild : function(node){
25062         var index = this.childNodes.indexOf(node);
25063         if(index == -1){
25064             return false;
25065         }
25066         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25067             return false;
25068         }
25069
25070         // remove it from childNodes collection
25071         this.childNodes.splice(index, 1);
25072
25073         // update siblings
25074         if(node.previousSibling){
25075             node.previousSibling.nextSibling = node.nextSibling;
25076         }
25077         if(node.nextSibling){
25078             node.nextSibling.previousSibling = node.previousSibling;
25079         }
25080
25081         // update child refs
25082         if(this.firstChild == node){
25083             this.setFirstChild(node.nextSibling);
25084         }
25085         if(this.lastChild == node){
25086             this.setLastChild(node.previousSibling);
25087         }
25088
25089         node.setOwnerTree(null);
25090         // clear any references from the node
25091         node.parentNode = null;
25092         node.previousSibling = null;
25093         node.nextSibling = null;
25094         this.fireEvent("remove", this.ownerTree, this, node);
25095         return node;
25096     },
25097
25098     /**
25099      * Inserts the first node before the second node in this nodes childNodes collection.
25100      * @param {Node} node The node to insert
25101      * @param {Node} refNode The node to insert before (if null the node is appended)
25102      * @return {Node} The inserted node
25103      */
25104     insertBefore : function(node, refNode){
25105         if(!refNode){ // like standard Dom, refNode can be null for append
25106             return this.appendChild(node);
25107         }
25108         // nothing to do
25109         if(node == refNode){
25110             return false;
25111         }
25112
25113         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25114             return false;
25115         }
25116         var index = this.childNodes.indexOf(refNode);
25117         var oldParent = node.parentNode;
25118         var refIndex = index;
25119
25120         // when moving internally, indexes will change after remove
25121         if(oldParent == this && this.childNodes.indexOf(node) < index){
25122             refIndex--;
25123         }
25124
25125         // it's a move, make sure we move it cleanly
25126         if(oldParent){
25127             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25128                 return false;
25129             }
25130             oldParent.removeChild(node);
25131         }
25132         if(refIndex == 0){
25133             this.setFirstChild(node);
25134         }
25135         this.childNodes.splice(refIndex, 0, node);
25136         node.parentNode = this;
25137         var ps = this.childNodes[refIndex-1];
25138         if(ps){
25139             node.previousSibling = ps;
25140             ps.nextSibling = node;
25141         }else{
25142             node.previousSibling = null;
25143         }
25144         node.nextSibling = refNode;
25145         refNode.previousSibling = node;
25146         node.setOwnerTree(this.getOwnerTree());
25147         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25148         if(oldParent){
25149             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25150         }
25151         return node;
25152     },
25153
25154     /**
25155      * Returns the child node at the specified index.
25156      * @param {Number} index
25157      * @return {Node}
25158      */
25159     item : function(index){
25160         return this.childNodes[index];
25161     },
25162
25163     /**
25164      * Replaces one child node in this node with another.
25165      * @param {Node} newChild The replacement node
25166      * @param {Node} oldChild The node to replace
25167      * @return {Node} The replaced node
25168      */
25169     replaceChild : function(newChild, oldChild){
25170         this.insertBefore(newChild, oldChild);
25171         this.removeChild(oldChild);
25172         return oldChild;
25173     },
25174
25175     /**
25176      * Returns the index of a child node
25177      * @param {Node} node
25178      * @return {Number} The index of the node or -1 if it was not found
25179      */
25180     indexOf : function(child){
25181         return this.childNodes.indexOf(child);
25182     },
25183
25184     /**
25185      * Returns the tree this node is in.
25186      * @return {Tree}
25187      */
25188     getOwnerTree : function(){
25189         // if it doesn't have one, look for one
25190         if(!this.ownerTree){
25191             var p = this;
25192             while(p){
25193                 if(p.ownerTree){
25194                     this.ownerTree = p.ownerTree;
25195                     break;
25196                 }
25197                 p = p.parentNode;
25198             }
25199         }
25200         return this.ownerTree;
25201     },
25202
25203     /**
25204      * Returns depth of this node (the root node has a depth of 0)
25205      * @return {Number}
25206      */
25207     getDepth : function(){
25208         var depth = 0;
25209         var p = this;
25210         while(p.parentNode){
25211             ++depth;
25212             p = p.parentNode;
25213         }
25214         return depth;
25215     },
25216
25217     // private
25218     setOwnerTree : function(tree){
25219         // if it's move, we need to update everyone
25220         if(tree != this.ownerTree){
25221             if(this.ownerTree){
25222                 this.ownerTree.unregisterNode(this);
25223             }
25224             this.ownerTree = tree;
25225             var cs = this.childNodes;
25226             for(var i = 0, len = cs.length; i < len; i++) {
25227                 cs[i].setOwnerTree(tree);
25228             }
25229             if(tree){
25230                 tree.registerNode(this);
25231             }
25232         }
25233     },
25234
25235     /**
25236      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25237      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25238      * @return {String} The path
25239      */
25240     getPath : function(attr){
25241         attr = attr || "id";
25242         var p = this.parentNode;
25243         var b = [this.attributes[attr]];
25244         while(p){
25245             b.unshift(p.attributes[attr]);
25246             p = p.parentNode;
25247         }
25248         var sep = this.getOwnerTree().pathSeparator;
25249         return sep + b.join(sep);
25250     },
25251
25252     /**
25253      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25254      * function call will be the scope provided or the current node. The arguments to the function
25255      * will be the args provided or the current node. If the function returns false at any point,
25256      * the bubble is stopped.
25257      * @param {Function} fn The function to call
25258      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25259      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25260      */
25261     bubble : function(fn, scope, args){
25262         var p = this;
25263         while(p){
25264             if(fn.call(scope || p, args || p) === false){
25265                 break;
25266             }
25267             p = p.parentNode;
25268         }
25269     },
25270
25271     /**
25272      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25273      * function call will be the scope provided or the current node. The arguments to the function
25274      * will be the args provided or the current node. If the function returns false at any point,
25275      * the cascade is stopped on that branch.
25276      * @param {Function} fn The function to call
25277      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25278      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25279      */
25280     cascade : function(fn, scope, args){
25281         if(fn.call(scope || this, args || this) !== false){
25282             var cs = this.childNodes;
25283             for(var i = 0, len = cs.length; i < len; i++) {
25284                 cs[i].cascade(fn, scope, args);
25285             }
25286         }
25287     },
25288
25289     /**
25290      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25291      * function call will be the scope provided or the current node. The arguments to the function
25292      * will be the args provided or the current node. If the function returns false at any point,
25293      * the iteration stops.
25294      * @param {Function} fn The function to call
25295      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25296      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25297      */
25298     eachChild : function(fn, scope, args){
25299         var cs = this.childNodes;
25300         for(var i = 0, len = cs.length; i < len; i++) {
25301                 if(fn.call(scope || this, args || cs[i]) === false){
25302                     break;
25303                 }
25304         }
25305     },
25306
25307     /**
25308      * Finds the first child that has the attribute with the specified value.
25309      * @param {String} attribute The attribute name
25310      * @param {Mixed} value The value to search for
25311      * @return {Node} The found child or null if none was found
25312      */
25313     findChild : function(attribute, value){
25314         var cs = this.childNodes;
25315         for(var i = 0, len = cs.length; i < len; i++) {
25316                 if(cs[i].attributes[attribute] == value){
25317                     return cs[i];
25318                 }
25319         }
25320         return null;
25321     },
25322
25323     /**
25324      * Finds the first child by a custom function. The child matches if the function passed
25325      * returns true.
25326      * @param {Function} fn
25327      * @param {Object} scope (optional)
25328      * @return {Node} The found child or null if none was found
25329      */
25330     findChildBy : function(fn, scope){
25331         var cs = this.childNodes;
25332         for(var i = 0, len = cs.length; i < len; i++) {
25333                 if(fn.call(scope||cs[i], cs[i]) === true){
25334                     return cs[i];
25335                 }
25336         }
25337         return null;
25338     },
25339
25340     /**
25341      * Sorts this nodes children using the supplied sort function
25342      * @param {Function} fn
25343      * @param {Object} scope (optional)
25344      */
25345     sort : function(fn, scope){
25346         var cs = this.childNodes;
25347         var len = cs.length;
25348         if(len > 0){
25349             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25350             cs.sort(sortFn);
25351             for(var i = 0; i < len; i++){
25352                 var n = cs[i];
25353                 n.previousSibling = cs[i-1];
25354                 n.nextSibling = cs[i+1];
25355                 if(i == 0){
25356                     this.setFirstChild(n);
25357                 }
25358                 if(i == len-1){
25359                     this.setLastChild(n);
25360                 }
25361             }
25362         }
25363     },
25364
25365     /**
25366      * Returns true if this node is an ancestor (at any point) of the passed node.
25367      * @param {Node} node
25368      * @return {Boolean}
25369      */
25370     contains : function(node){
25371         return node.isAncestor(this);
25372     },
25373
25374     /**
25375      * Returns true if the passed node is an ancestor (at any point) of this node.
25376      * @param {Node} node
25377      * @return {Boolean}
25378      */
25379     isAncestor : function(node){
25380         var p = this.parentNode;
25381         while(p){
25382             if(p == node){
25383                 return true;
25384             }
25385             p = p.parentNode;
25386         }
25387         return false;
25388     },
25389
25390     toString : function(){
25391         return "[Node"+(this.id?" "+this.id:"")+"]";
25392     }
25393 });/*
25394  * Based on:
25395  * Ext JS Library 1.1.1
25396  * Copyright(c) 2006-2007, Ext JS, LLC.
25397  *
25398  * Originally Released Under LGPL - original licence link has changed is not relivant.
25399  *
25400  * Fork - LGPL
25401  * <script type="text/javascript">
25402  */
25403  (function(){ 
25404 /**
25405  * @class Roo.Layer
25406  * @extends Roo.Element
25407  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25408  * automatic maintaining of shadow/shim positions.
25409  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25410  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25411  * you can pass a string with a CSS class name. False turns off the shadow.
25412  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25413  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25414  * @cfg {String} cls CSS class to add to the element
25415  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25416  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25417  * @constructor
25418  * @param {Object} config An object with config options.
25419  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25420  */
25421
25422 Roo.Layer = function(config, existingEl){
25423     config = config || {};
25424     var dh = Roo.DomHelper;
25425     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25426     if(existingEl){
25427         this.dom = Roo.getDom(existingEl);
25428     }
25429     if(!this.dom){
25430         var o = config.dh || {tag: "div", cls: "x-layer"};
25431         this.dom = dh.append(pel, o);
25432     }
25433     if(config.cls){
25434         this.addClass(config.cls);
25435     }
25436     this.constrain = config.constrain !== false;
25437     this.visibilityMode = Roo.Element.VISIBILITY;
25438     if(config.id){
25439         this.id = this.dom.id = config.id;
25440     }else{
25441         this.id = Roo.id(this.dom);
25442     }
25443     this.zindex = config.zindex || this.getZIndex();
25444     this.position("absolute", this.zindex);
25445     if(config.shadow){
25446         this.shadowOffset = config.shadowOffset || 4;
25447         this.shadow = new Roo.Shadow({
25448             offset : this.shadowOffset,
25449             mode : config.shadow
25450         });
25451     }else{
25452         this.shadowOffset = 0;
25453     }
25454     this.useShim = config.shim !== false && Roo.useShims;
25455     this.useDisplay = config.useDisplay;
25456     this.hide();
25457 };
25458
25459 var supr = Roo.Element.prototype;
25460
25461 // shims are shared among layer to keep from having 100 iframes
25462 var shims = [];
25463
25464 Roo.extend(Roo.Layer, Roo.Element, {
25465
25466     getZIndex : function(){
25467         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25468     },
25469
25470     getShim : function(){
25471         if(!this.useShim){
25472             return null;
25473         }
25474         if(this.shim){
25475             return this.shim;
25476         }
25477         var shim = shims.shift();
25478         if(!shim){
25479             shim = this.createShim();
25480             shim.enableDisplayMode('block');
25481             shim.dom.style.display = 'none';
25482             shim.dom.style.visibility = 'visible';
25483         }
25484         var pn = this.dom.parentNode;
25485         if(shim.dom.parentNode != pn){
25486             pn.insertBefore(shim.dom, this.dom);
25487         }
25488         shim.setStyle('z-index', this.getZIndex()-2);
25489         this.shim = shim;
25490         return shim;
25491     },
25492
25493     hideShim : function(){
25494         if(this.shim){
25495             this.shim.setDisplayed(false);
25496             shims.push(this.shim);
25497             delete this.shim;
25498         }
25499     },
25500
25501     disableShadow : function(){
25502         if(this.shadow){
25503             this.shadowDisabled = true;
25504             this.shadow.hide();
25505             this.lastShadowOffset = this.shadowOffset;
25506             this.shadowOffset = 0;
25507         }
25508     },
25509
25510     enableShadow : function(show){
25511         if(this.shadow){
25512             this.shadowDisabled = false;
25513             this.shadowOffset = this.lastShadowOffset;
25514             delete this.lastShadowOffset;
25515             if(show){
25516                 this.sync(true);
25517             }
25518         }
25519     },
25520
25521     // private
25522     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25523     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25524     sync : function(doShow){
25525         var sw = this.shadow;
25526         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25527             var sh = this.getShim();
25528
25529             var w = this.getWidth(),
25530                 h = this.getHeight();
25531
25532             var l = this.getLeft(true),
25533                 t = this.getTop(true);
25534
25535             if(sw && !this.shadowDisabled){
25536                 if(doShow && !sw.isVisible()){
25537                     sw.show(this);
25538                 }else{
25539                     sw.realign(l, t, w, h);
25540                 }
25541                 if(sh){
25542                     if(doShow){
25543                        sh.show();
25544                     }
25545                     // fit the shim behind the shadow, so it is shimmed too
25546                     var a = sw.adjusts, s = sh.dom.style;
25547                     s.left = (Math.min(l, l+a.l))+"px";
25548                     s.top = (Math.min(t, t+a.t))+"px";
25549                     s.width = (w+a.w)+"px";
25550                     s.height = (h+a.h)+"px";
25551                 }
25552             }else if(sh){
25553                 if(doShow){
25554                    sh.show();
25555                 }
25556                 sh.setSize(w, h);
25557                 sh.setLeftTop(l, t);
25558             }
25559             
25560         }
25561     },
25562
25563     // private
25564     destroy : function(){
25565         this.hideShim();
25566         if(this.shadow){
25567             this.shadow.hide();
25568         }
25569         this.removeAllListeners();
25570         var pn = this.dom.parentNode;
25571         if(pn){
25572             pn.removeChild(this.dom);
25573         }
25574         Roo.Element.uncache(this.id);
25575     },
25576
25577     remove : function(){
25578         this.destroy();
25579     },
25580
25581     // private
25582     beginUpdate : function(){
25583         this.updating = true;
25584     },
25585
25586     // private
25587     endUpdate : function(){
25588         this.updating = false;
25589         this.sync(true);
25590     },
25591
25592     // private
25593     hideUnders : function(negOffset){
25594         if(this.shadow){
25595             this.shadow.hide();
25596         }
25597         this.hideShim();
25598     },
25599
25600     // private
25601     constrainXY : function(){
25602         if(this.constrain){
25603             var vw = Roo.lib.Dom.getViewWidth(),
25604                 vh = Roo.lib.Dom.getViewHeight();
25605             var s = Roo.get(document).getScroll();
25606
25607             var xy = this.getXY();
25608             var x = xy[0], y = xy[1];   
25609             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25610             // only move it if it needs it
25611             var moved = false;
25612             // first validate right/bottom
25613             if((x + w) > vw+s.left){
25614                 x = vw - w - this.shadowOffset;
25615                 moved = true;
25616             }
25617             if((y + h) > vh+s.top){
25618                 y = vh - h - this.shadowOffset;
25619                 moved = true;
25620             }
25621             // then make sure top/left isn't negative
25622             if(x < s.left){
25623                 x = s.left;
25624                 moved = true;
25625             }
25626             if(y < s.top){
25627                 y = s.top;
25628                 moved = true;
25629             }
25630             if(moved){
25631                 if(this.avoidY){
25632                     var ay = this.avoidY;
25633                     if(y <= ay && (y+h) >= ay){
25634                         y = ay-h-5;   
25635                     }
25636                 }
25637                 xy = [x, y];
25638                 this.storeXY(xy);
25639                 supr.setXY.call(this, xy);
25640                 this.sync();
25641             }
25642         }
25643     },
25644
25645     isVisible : function(){
25646         return this.visible;    
25647     },
25648
25649     // private
25650     showAction : function(){
25651         this.visible = true; // track visibility to prevent getStyle calls
25652         if(this.useDisplay === true){
25653             this.setDisplayed("");
25654         }else if(this.lastXY){
25655             supr.setXY.call(this, this.lastXY);
25656         }else if(this.lastLT){
25657             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25658         }
25659     },
25660
25661     // private
25662     hideAction : function(){
25663         this.visible = false;
25664         if(this.useDisplay === true){
25665             this.setDisplayed(false);
25666         }else{
25667             this.setLeftTop(-10000,-10000);
25668         }
25669     },
25670
25671     // overridden Element method
25672     setVisible : function(v, a, d, c, e){
25673         if(v){
25674             this.showAction();
25675         }
25676         if(a && v){
25677             var cb = function(){
25678                 this.sync(true);
25679                 if(c){
25680                     c();
25681                 }
25682             }.createDelegate(this);
25683             supr.setVisible.call(this, true, true, d, cb, e);
25684         }else{
25685             if(!v){
25686                 this.hideUnders(true);
25687             }
25688             var cb = c;
25689             if(a){
25690                 cb = function(){
25691                     this.hideAction();
25692                     if(c){
25693                         c();
25694                     }
25695                 }.createDelegate(this);
25696             }
25697             supr.setVisible.call(this, v, a, d, cb, e);
25698             if(v){
25699                 this.sync(true);
25700             }else if(!a){
25701                 this.hideAction();
25702             }
25703         }
25704     },
25705
25706     storeXY : function(xy){
25707         delete this.lastLT;
25708         this.lastXY = xy;
25709     },
25710
25711     storeLeftTop : function(left, top){
25712         delete this.lastXY;
25713         this.lastLT = [left, top];
25714     },
25715
25716     // private
25717     beforeFx : function(){
25718         this.beforeAction();
25719         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25720     },
25721
25722     // private
25723     afterFx : function(){
25724         Roo.Layer.superclass.afterFx.apply(this, arguments);
25725         this.sync(this.isVisible());
25726     },
25727
25728     // private
25729     beforeAction : function(){
25730         if(!this.updating && this.shadow){
25731             this.shadow.hide();
25732         }
25733     },
25734
25735     // overridden Element method
25736     setLeft : function(left){
25737         this.storeLeftTop(left, this.getTop(true));
25738         supr.setLeft.apply(this, arguments);
25739         this.sync();
25740     },
25741
25742     setTop : function(top){
25743         this.storeLeftTop(this.getLeft(true), top);
25744         supr.setTop.apply(this, arguments);
25745         this.sync();
25746     },
25747
25748     setLeftTop : function(left, top){
25749         this.storeLeftTop(left, top);
25750         supr.setLeftTop.apply(this, arguments);
25751         this.sync();
25752     },
25753
25754     setXY : function(xy, a, d, c, e){
25755         this.fixDisplay();
25756         this.beforeAction();
25757         this.storeXY(xy);
25758         var cb = this.createCB(c);
25759         supr.setXY.call(this, xy, a, d, cb, e);
25760         if(!a){
25761             cb();
25762         }
25763     },
25764
25765     // private
25766     createCB : function(c){
25767         var el = this;
25768         return function(){
25769             el.constrainXY();
25770             el.sync(true);
25771             if(c){
25772                 c();
25773             }
25774         };
25775     },
25776
25777     // overridden Element method
25778     setX : function(x, a, d, c, e){
25779         this.setXY([x, this.getY()], a, d, c, e);
25780     },
25781
25782     // overridden Element method
25783     setY : function(y, a, d, c, e){
25784         this.setXY([this.getX(), y], a, d, c, e);
25785     },
25786
25787     // overridden Element method
25788     setSize : function(w, h, a, d, c, e){
25789         this.beforeAction();
25790         var cb = this.createCB(c);
25791         supr.setSize.call(this, w, h, a, d, cb, e);
25792         if(!a){
25793             cb();
25794         }
25795     },
25796
25797     // overridden Element method
25798     setWidth : function(w, a, d, c, e){
25799         this.beforeAction();
25800         var cb = this.createCB(c);
25801         supr.setWidth.call(this, w, a, d, cb, e);
25802         if(!a){
25803             cb();
25804         }
25805     },
25806
25807     // overridden Element method
25808     setHeight : function(h, a, d, c, e){
25809         this.beforeAction();
25810         var cb = this.createCB(c);
25811         supr.setHeight.call(this, h, a, d, cb, e);
25812         if(!a){
25813             cb();
25814         }
25815     },
25816
25817     // overridden Element method
25818     setBounds : function(x, y, w, h, a, d, c, e){
25819         this.beforeAction();
25820         var cb = this.createCB(c);
25821         if(!a){
25822             this.storeXY([x, y]);
25823             supr.setXY.call(this, [x, y]);
25824             supr.setSize.call(this, w, h, a, d, cb, e);
25825             cb();
25826         }else{
25827             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25828         }
25829         return this;
25830     },
25831     
25832     /**
25833      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25834      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25835      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25836      * @param {Number} zindex The new z-index to set
25837      * @return {this} The Layer
25838      */
25839     setZIndex : function(zindex){
25840         this.zindex = zindex;
25841         this.setStyle("z-index", zindex + 2);
25842         if(this.shadow){
25843             this.shadow.setZIndex(zindex + 1);
25844         }
25845         if(this.shim){
25846             this.shim.setStyle("z-index", zindex);
25847         }
25848     }
25849 });
25850 })();/*
25851  * Based on:
25852  * Ext JS Library 1.1.1
25853  * Copyright(c) 2006-2007, Ext JS, LLC.
25854  *
25855  * Originally Released Under LGPL - original licence link has changed is not relivant.
25856  *
25857  * Fork - LGPL
25858  * <script type="text/javascript">
25859  */
25860
25861
25862 /**
25863  * @class Roo.Shadow
25864  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25865  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25866  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25867  * @constructor
25868  * Create a new Shadow
25869  * @param {Object} config The config object
25870  */
25871 Roo.Shadow = function(config){
25872     Roo.apply(this, config);
25873     if(typeof this.mode != "string"){
25874         this.mode = this.defaultMode;
25875     }
25876     var o = this.offset, a = {h: 0};
25877     var rad = Math.floor(this.offset/2);
25878     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25879         case "drop":
25880             a.w = 0;
25881             a.l = a.t = o;
25882             a.t -= 1;
25883             if(Roo.isIE){
25884                 a.l -= this.offset + rad;
25885                 a.t -= this.offset + rad;
25886                 a.w -= rad;
25887                 a.h -= rad;
25888                 a.t += 1;
25889             }
25890         break;
25891         case "sides":
25892             a.w = (o*2);
25893             a.l = -o;
25894             a.t = o-1;
25895             if(Roo.isIE){
25896                 a.l -= (this.offset - rad);
25897                 a.t -= this.offset + rad;
25898                 a.l += 1;
25899                 a.w -= (this.offset - rad)*2;
25900                 a.w -= rad + 1;
25901                 a.h -= 1;
25902             }
25903         break;
25904         case "frame":
25905             a.w = a.h = (o*2);
25906             a.l = a.t = -o;
25907             a.t += 1;
25908             a.h -= 2;
25909             if(Roo.isIE){
25910                 a.l -= (this.offset - rad);
25911                 a.t -= (this.offset - rad);
25912                 a.l += 1;
25913                 a.w -= (this.offset + rad + 1);
25914                 a.h -= (this.offset + rad);
25915                 a.h += 1;
25916             }
25917         break;
25918     };
25919
25920     this.adjusts = a;
25921 };
25922
25923 Roo.Shadow.prototype = {
25924     /**
25925      * @cfg {String} mode
25926      * The shadow display mode.  Supports the following options:<br />
25927      * sides: Shadow displays on both sides and bottom only<br />
25928      * frame: Shadow displays equally on all four sides<br />
25929      * drop: Traditional bottom-right drop shadow (default)
25930      */
25931     /**
25932      * @cfg {String} offset
25933      * The number of pixels to offset the shadow from the element (defaults to 4)
25934      */
25935     offset: 4,
25936
25937     // private
25938     defaultMode: "drop",
25939
25940     /**
25941      * Displays the shadow under the target element
25942      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25943      */
25944     show : function(target){
25945         target = Roo.get(target);
25946         if(!this.el){
25947             this.el = Roo.Shadow.Pool.pull();
25948             if(this.el.dom.nextSibling != target.dom){
25949                 this.el.insertBefore(target);
25950             }
25951         }
25952         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25953         if(Roo.isIE){
25954             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25955         }
25956         this.realign(
25957             target.getLeft(true),
25958             target.getTop(true),
25959             target.getWidth(),
25960             target.getHeight()
25961         );
25962         this.el.dom.style.display = "block";
25963     },
25964
25965     /**
25966      * Returns true if the shadow is visible, else false
25967      */
25968     isVisible : function(){
25969         return this.el ? true : false;  
25970     },
25971
25972     /**
25973      * Direct alignment when values are already available. Show must be called at least once before
25974      * calling this method to ensure it is initialized.
25975      * @param {Number} left The target element left position
25976      * @param {Number} top The target element top position
25977      * @param {Number} width The target element width
25978      * @param {Number} height The target element height
25979      */
25980     realign : function(l, t, w, h){
25981         if(!this.el){
25982             return;
25983         }
25984         var a = this.adjusts, d = this.el.dom, s = d.style;
25985         var iea = 0;
25986         s.left = (l+a.l)+"px";
25987         s.top = (t+a.t)+"px";
25988         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25989  
25990         if(s.width != sws || s.height != shs){
25991             s.width = sws;
25992             s.height = shs;
25993             if(!Roo.isIE){
25994                 var cn = d.childNodes;
25995                 var sww = Math.max(0, (sw-12))+"px";
25996                 cn[0].childNodes[1].style.width = sww;
25997                 cn[1].childNodes[1].style.width = sww;
25998                 cn[2].childNodes[1].style.width = sww;
25999                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26000             }
26001         }
26002     },
26003
26004     /**
26005      * Hides this shadow
26006      */
26007     hide : function(){
26008         if(this.el){
26009             this.el.dom.style.display = "none";
26010             Roo.Shadow.Pool.push(this.el);
26011             delete this.el;
26012         }
26013     },
26014
26015     /**
26016      * Adjust the z-index of this shadow
26017      * @param {Number} zindex The new z-index
26018      */
26019     setZIndex : function(z){
26020         this.zIndex = z;
26021         if(this.el){
26022             this.el.setStyle("z-index", z);
26023         }
26024     }
26025 };
26026
26027 // Private utility class that manages the internal Shadow cache
26028 Roo.Shadow.Pool = function(){
26029     var p = [];
26030     var markup = Roo.isIE ?
26031                  '<div class="x-ie-shadow"></div>' :
26032                  '<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>';
26033     return {
26034         pull : function(){
26035             var sh = p.shift();
26036             if(!sh){
26037                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26038                 sh.autoBoxAdjust = false;
26039             }
26040             return sh;
26041         },
26042
26043         push : function(sh){
26044             p.push(sh);
26045         }
26046     };
26047 }();/*
26048  * Based on:
26049  * Ext JS Library 1.1.1
26050  * Copyright(c) 2006-2007, Ext JS, LLC.
26051  *
26052  * Originally Released Under LGPL - original licence link has changed is not relivant.
26053  *
26054  * Fork - LGPL
26055  * <script type="text/javascript">
26056  */
26057
26058
26059 /**
26060  * @class Roo.SplitBar
26061  * @extends Roo.util.Observable
26062  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26063  * <br><br>
26064  * Usage:
26065  * <pre><code>
26066 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26067                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26068 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26069 split.minSize = 100;
26070 split.maxSize = 600;
26071 split.animate = true;
26072 split.on('moved', splitterMoved);
26073 </code></pre>
26074  * @constructor
26075  * Create a new SplitBar
26076  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26077  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26078  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26079  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26080                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26081                         position of the SplitBar).
26082  */
26083 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26084     
26085     /** @private */
26086     this.el = Roo.get(dragElement, true);
26087     this.el.dom.unselectable = "on";
26088     /** @private */
26089     this.resizingEl = Roo.get(resizingElement, true);
26090
26091     /**
26092      * @private
26093      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26094      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26095      * @type Number
26096      */
26097     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26098     
26099     /**
26100      * The minimum size of the resizing element. (Defaults to 0)
26101      * @type Number
26102      */
26103     this.minSize = 0;
26104     
26105     /**
26106      * The maximum size of the resizing element. (Defaults to 2000)
26107      * @type Number
26108      */
26109     this.maxSize = 2000;
26110     
26111     /**
26112      * Whether to animate the transition to the new size
26113      * @type Boolean
26114      */
26115     this.animate = false;
26116     
26117     /**
26118      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26119      * @type Boolean
26120      */
26121     this.useShim = false;
26122     
26123     /** @private */
26124     this.shim = null;
26125     
26126     if(!existingProxy){
26127         /** @private */
26128         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26129     }else{
26130         this.proxy = Roo.get(existingProxy).dom;
26131     }
26132     /** @private */
26133     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26134     
26135     /** @private */
26136     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26137     
26138     /** @private */
26139     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26140     
26141     /** @private */
26142     this.dragSpecs = {};
26143     
26144     /**
26145      * @private The adapter to use to positon and resize elements
26146      */
26147     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26148     this.adapter.init(this);
26149     
26150     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26151         /** @private */
26152         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26153         this.el.addClass("x-splitbar-h");
26154     }else{
26155         /** @private */
26156         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26157         this.el.addClass("x-splitbar-v");
26158     }
26159     
26160     this.addEvents({
26161         /**
26162          * @event resize
26163          * Fires when the splitter is moved (alias for {@link #event-moved})
26164          * @param {Roo.SplitBar} this
26165          * @param {Number} newSize the new width or height
26166          */
26167         "resize" : true,
26168         /**
26169          * @event moved
26170          * Fires when the splitter is moved
26171          * @param {Roo.SplitBar} this
26172          * @param {Number} newSize the new width or height
26173          */
26174         "moved" : true,
26175         /**
26176          * @event beforeresize
26177          * Fires before the splitter is dragged
26178          * @param {Roo.SplitBar} this
26179          */
26180         "beforeresize" : true,
26181
26182         "beforeapply" : true
26183     });
26184
26185     Roo.util.Observable.call(this);
26186 };
26187
26188 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26189     onStartProxyDrag : function(x, y){
26190         this.fireEvent("beforeresize", this);
26191         if(!this.overlay){
26192             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26193             o.unselectable();
26194             o.enableDisplayMode("block");
26195             // all splitbars share the same overlay
26196             Roo.SplitBar.prototype.overlay = o;
26197         }
26198         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26199         this.overlay.show();
26200         Roo.get(this.proxy).setDisplayed("block");
26201         var size = this.adapter.getElementSize(this);
26202         this.activeMinSize = this.getMinimumSize();;
26203         this.activeMaxSize = this.getMaximumSize();;
26204         var c1 = size - this.activeMinSize;
26205         var c2 = Math.max(this.activeMaxSize - size, 0);
26206         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26207             this.dd.resetConstraints();
26208             this.dd.setXConstraint(
26209                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26210                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26211             );
26212             this.dd.setYConstraint(0, 0);
26213         }else{
26214             this.dd.resetConstraints();
26215             this.dd.setXConstraint(0, 0);
26216             this.dd.setYConstraint(
26217                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26218                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26219             );
26220          }
26221         this.dragSpecs.startSize = size;
26222         this.dragSpecs.startPoint = [x, y];
26223         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26224     },
26225     
26226     /** 
26227      * @private Called after the drag operation by the DDProxy
26228      */
26229     onEndProxyDrag : function(e){
26230         Roo.get(this.proxy).setDisplayed(false);
26231         var endPoint = Roo.lib.Event.getXY(e);
26232         if(this.overlay){
26233             this.overlay.hide();
26234         }
26235         var newSize;
26236         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26237             newSize = this.dragSpecs.startSize + 
26238                 (this.placement == Roo.SplitBar.LEFT ?
26239                     endPoint[0] - this.dragSpecs.startPoint[0] :
26240                     this.dragSpecs.startPoint[0] - endPoint[0]
26241                 );
26242         }else{
26243             newSize = this.dragSpecs.startSize + 
26244                 (this.placement == Roo.SplitBar.TOP ?
26245                     endPoint[1] - this.dragSpecs.startPoint[1] :
26246                     this.dragSpecs.startPoint[1] - endPoint[1]
26247                 );
26248         }
26249         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26250         if(newSize != this.dragSpecs.startSize){
26251             if(this.fireEvent('beforeapply', this, newSize) !== false){
26252                 this.adapter.setElementSize(this, newSize);
26253                 this.fireEvent("moved", this, newSize);
26254                 this.fireEvent("resize", this, newSize);
26255             }
26256         }
26257     },
26258     
26259     /**
26260      * Get the adapter this SplitBar uses
26261      * @return The adapter object
26262      */
26263     getAdapter : function(){
26264         return this.adapter;
26265     },
26266     
26267     /**
26268      * Set the adapter this SplitBar uses
26269      * @param {Object} adapter A SplitBar adapter object
26270      */
26271     setAdapter : function(adapter){
26272         this.adapter = adapter;
26273         this.adapter.init(this);
26274     },
26275     
26276     /**
26277      * Gets the minimum size for the resizing element
26278      * @return {Number} The minimum size
26279      */
26280     getMinimumSize : function(){
26281         return this.minSize;
26282     },
26283     
26284     /**
26285      * Sets the minimum size for the resizing element
26286      * @param {Number} minSize The minimum size
26287      */
26288     setMinimumSize : function(minSize){
26289         this.minSize = minSize;
26290     },
26291     
26292     /**
26293      * Gets the maximum size for the resizing element
26294      * @return {Number} The maximum size
26295      */
26296     getMaximumSize : function(){
26297         return this.maxSize;
26298     },
26299     
26300     /**
26301      * Sets the maximum size for the resizing element
26302      * @param {Number} maxSize The maximum size
26303      */
26304     setMaximumSize : function(maxSize){
26305         this.maxSize = maxSize;
26306     },
26307     
26308     /**
26309      * Sets the initialize size for the resizing element
26310      * @param {Number} size The initial size
26311      */
26312     setCurrentSize : function(size){
26313         var oldAnimate = this.animate;
26314         this.animate = false;
26315         this.adapter.setElementSize(this, size);
26316         this.animate = oldAnimate;
26317     },
26318     
26319     /**
26320      * Destroy this splitbar. 
26321      * @param {Boolean} removeEl True to remove the element
26322      */
26323     destroy : function(removeEl){
26324         if(this.shim){
26325             this.shim.remove();
26326         }
26327         this.dd.unreg();
26328         this.proxy.parentNode.removeChild(this.proxy);
26329         if(removeEl){
26330             this.el.remove();
26331         }
26332     }
26333 });
26334
26335 /**
26336  * @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.
26337  */
26338 Roo.SplitBar.createProxy = function(dir){
26339     var proxy = new Roo.Element(document.createElement("div"));
26340     proxy.unselectable();
26341     var cls = 'x-splitbar-proxy';
26342     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26343     document.body.appendChild(proxy.dom);
26344     return proxy.dom;
26345 };
26346
26347 /** 
26348  * @class Roo.SplitBar.BasicLayoutAdapter
26349  * Default Adapter. It assumes the splitter and resizing element are not positioned
26350  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26351  */
26352 Roo.SplitBar.BasicLayoutAdapter = function(){
26353 };
26354
26355 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26356     // do nothing for now
26357     init : function(s){
26358     
26359     },
26360     /**
26361      * Called before drag operations to get the current size of the resizing element. 
26362      * @param {Roo.SplitBar} s The SplitBar using this adapter
26363      */
26364      getElementSize : function(s){
26365         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26366             return s.resizingEl.getWidth();
26367         }else{
26368             return s.resizingEl.getHeight();
26369         }
26370     },
26371     
26372     /**
26373      * Called after drag operations to set the size of the resizing element.
26374      * @param {Roo.SplitBar} s The SplitBar using this adapter
26375      * @param {Number} newSize The new size to set
26376      * @param {Function} onComplete A function to be invoked when resizing is complete
26377      */
26378     setElementSize : function(s, newSize, onComplete){
26379         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26380             if(!s.animate){
26381                 s.resizingEl.setWidth(newSize);
26382                 if(onComplete){
26383                     onComplete(s, newSize);
26384                 }
26385             }else{
26386                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26387             }
26388         }else{
26389             
26390             if(!s.animate){
26391                 s.resizingEl.setHeight(newSize);
26392                 if(onComplete){
26393                     onComplete(s, newSize);
26394                 }
26395             }else{
26396                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26397             }
26398         }
26399     }
26400 };
26401
26402 /** 
26403  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26404  * @extends Roo.SplitBar.BasicLayoutAdapter
26405  * Adapter that  moves the splitter element to align with the resized sizing element. 
26406  * Used with an absolute positioned SplitBar.
26407  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26408  * document.body, make sure you assign an id to the body element.
26409  */
26410 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26411     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26412     this.container = Roo.get(container);
26413 };
26414
26415 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26416     init : function(s){
26417         this.basic.init(s);
26418     },
26419     
26420     getElementSize : function(s){
26421         return this.basic.getElementSize(s);
26422     },
26423     
26424     setElementSize : function(s, newSize, onComplete){
26425         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26426     },
26427     
26428     moveSplitter : function(s){
26429         var yes = Roo.SplitBar;
26430         switch(s.placement){
26431             case yes.LEFT:
26432                 s.el.setX(s.resizingEl.getRight());
26433                 break;
26434             case yes.RIGHT:
26435                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26436                 break;
26437             case yes.TOP:
26438                 s.el.setY(s.resizingEl.getBottom());
26439                 break;
26440             case yes.BOTTOM:
26441                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26442                 break;
26443         }
26444     }
26445 };
26446
26447 /**
26448  * Orientation constant - Create a vertical SplitBar
26449  * @static
26450  * @type Number
26451  */
26452 Roo.SplitBar.VERTICAL = 1;
26453
26454 /**
26455  * Orientation constant - Create a horizontal SplitBar
26456  * @static
26457  * @type Number
26458  */
26459 Roo.SplitBar.HORIZONTAL = 2;
26460
26461 /**
26462  * Placement constant - The resizing element is to the left of the splitter element
26463  * @static
26464  * @type Number
26465  */
26466 Roo.SplitBar.LEFT = 1;
26467
26468 /**
26469  * Placement constant - The resizing element is to the right of the splitter element
26470  * @static
26471  * @type Number
26472  */
26473 Roo.SplitBar.RIGHT = 2;
26474
26475 /**
26476  * Placement constant - The resizing element is positioned above the splitter element
26477  * @static
26478  * @type Number
26479  */
26480 Roo.SplitBar.TOP = 3;
26481
26482 /**
26483  * Placement constant - The resizing element is positioned under splitter element
26484  * @static
26485  * @type Number
26486  */
26487 Roo.SplitBar.BOTTOM = 4;
26488 /*
26489  * Based on:
26490  * Ext JS Library 1.1.1
26491  * Copyright(c) 2006-2007, Ext JS, LLC.
26492  *
26493  * Originally Released Under LGPL - original licence link has changed is not relivant.
26494  *
26495  * Fork - LGPL
26496  * <script type="text/javascript">
26497  */
26498
26499 /**
26500  * @class Roo.View
26501  * @extends Roo.util.Observable
26502  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26503  * This class also supports single and multi selection modes. <br>
26504  * Create a data model bound view:
26505  <pre><code>
26506  var store = new Roo.data.Store(...);
26507
26508  var view = new Roo.View({
26509     el : "my-element",
26510     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26511  
26512     singleSelect: true,
26513     selectedClass: "ydataview-selected",
26514     store: store
26515  });
26516
26517  // listen for node click?
26518  view.on("click", function(vw, index, node, e){
26519  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26520  });
26521
26522  // load XML data
26523  dataModel.load("foobar.xml");
26524  </code></pre>
26525  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26526  * <br><br>
26527  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26528  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26529  * 
26530  * Note: old style constructor is still suported (container, template, config)
26531  * 
26532  * @constructor
26533  * Create a new View
26534  * @param {Object} config The config object
26535  * 
26536  */
26537 Roo.View = function(config, depreciated_tpl, depreciated_config){
26538     
26539     this.parent = false;
26540     
26541     if (typeof(depreciated_tpl) == 'undefined') {
26542         // new way.. - universal constructor.
26543         Roo.apply(this, config);
26544         this.el  = Roo.get(this.el);
26545     } else {
26546         // old format..
26547         this.el  = Roo.get(config);
26548         this.tpl = depreciated_tpl;
26549         Roo.apply(this, depreciated_config);
26550     }
26551     this.wrapEl  = this.el.wrap().wrap();
26552     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26553     
26554     
26555     if(typeof(this.tpl) == "string"){
26556         this.tpl = new Roo.Template(this.tpl);
26557     } else {
26558         // support xtype ctors..
26559         this.tpl = new Roo.factory(this.tpl, Roo);
26560     }
26561     
26562     
26563     this.tpl.compile();
26564     
26565     /** @private */
26566     this.addEvents({
26567         /**
26568          * @event beforeclick
26569          * Fires before a click is processed. Returns false to cancel the default action.
26570          * @param {Roo.View} this
26571          * @param {Number} index The index of the target node
26572          * @param {HTMLElement} node The target node
26573          * @param {Roo.EventObject} e The raw event object
26574          */
26575             "beforeclick" : true,
26576         /**
26577          * @event click
26578          * Fires when a template node is clicked.
26579          * @param {Roo.View} this
26580          * @param {Number} index The index of the target node
26581          * @param {HTMLElement} node The target node
26582          * @param {Roo.EventObject} e The raw event object
26583          */
26584             "click" : true,
26585         /**
26586          * @event dblclick
26587          * Fires when a template node is double clicked.
26588          * @param {Roo.View} this
26589          * @param {Number} index The index of the target node
26590          * @param {HTMLElement} node The target node
26591          * @param {Roo.EventObject} e The raw event object
26592          */
26593             "dblclick" : true,
26594         /**
26595          * @event contextmenu
26596          * Fires when a template node is right clicked.
26597          * @param {Roo.View} this
26598          * @param {Number} index The index of the target node
26599          * @param {HTMLElement} node The target node
26600          * @param {Roo.EventObject} e The raw event object
26601          */
26602             "contextmenu" : true,
26603         /**
26604          * @event selectionchange
26605          * Fires when the selected nodes change.
26606          * @param {Roo.View} this
26607          * @param {Array} selections Array of the selected nodes
26608          */
26609             "selectionchange" : true,
26610     
26611         /**
26612          * @event beforeselect
26613          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26614          * @param {Roo.View} this
26615          * @param {HTMLElement} node The node to be selected
26616          * @param {Array} selections Array of currently selected nodes
26617          */
26618             "beforeselect" : true,
26619         /**
26620          * @event preparedata
26621          * Fires on every row to render, to allow you to change the data.
26622          * @param {Roo.View} this
26623          * @param {Object} data to be rendered (change this)
26624          */
26625           "preparedata" : true
26626           
26627           
26628         });
26629
26630
26631
26632     this.el.on({
26633         "click": this.onClick,
26634         "dblclick": this.onDblClick,
26635         "contextmenu": this.onContextMenu,
26636         scope:this
26637     });
26638
26639     this.selections = [];
26640     this.nodes = [];
26641     this.cmp = new Roo.CompositeElementLite([]);
26642     if(this.store){
26643         this.store = Roo.factory(this.store, Roo.data);
26644         this.setStore(this.store, true);
26645     }
26646     
26647     if ( this.footer && this.footer.xtype) {
26648            
26649          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26650         
26651         this.footer.dataSource = this.store;
26652         this.footer.container = fctr;
26653         this.footer = Roo.factory(this.footer, Roo);
26654         fctr.insertFirst(this.el);
26655         
26656         // this is a bit insane - as the paging toolbar seems to detach the el..
26657 //        dom.parentNode.parentNode.parentNode
26658          // they get detached?
26659     }
26660     
26661     
26662     Roo.View.superclass.constructor.call(this);
26663     
26664     
26665 };
26666
26667 Roo.extend(Roo.View, Roo.util.Observable, {
26668     
26669      /**
26670      * @cfg {Roo.data.Store} store Data store to load data from.
26671      */
26672     store : false,
26673     
26674     /**
26675      * @cfg {String|Roo.Element} el The container element.
26676      */
26677     el : '',
26678     
26679     /**
26680      * @cfg {String|Roo.Template} tpl The template used by this View 
26681      */
26682     tpl : false,
26683     /**
26684      * @cfg {String} dataName the named area of the template to use as the data area
26685      *                          Works with domtemplates roo-name="name"
26686      */
26687     dataName: false,
26688     /**
26689      * @cfg {String} selectedClass The css class to add to selected nodes
26690      */
26691     selectedClass : "x-view-selected",
26692      /**
26693      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26694      */
26695     emptyText : "",
26696     
26697     /**
26698      * @cfg {String} text to display on mask (default Loading)
26699      */
26700     mask : false,
26701     /**
26702      * @cfg {Boolean} multiSelect Allow multiple selection
26703      */
26704     multiSelect : false,
26705     /**
26706      * @cfg {Boolean} singleSelect Allow single selection
26707      */
26708     singleSelect:  false,
26709     
26710     /**
26711      * @cfg {Boolean} toggleSelect - selecting 
26712      */
26713     toggleSelect : false,
26714     
26715     /**
26716      * @cfg {Boolean} tickable - selecting 
26717      */
26718     tickable : false,
26719     
26720     /**
26721      * Returns the element this view is bound to.
26722      * @return {Roo.Element}
26723      */
26724     getEl : function(){
26725         return this.wrapEl;
26726     },
26727     
26728     
26729
26730     /**
26731      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26732      */
26733     refresh : function(){
26734         //Roo.log('refresh');
26735         var t = this.tpl;
26736         
26737         // if we are using something like 'domtemplate', then
26738         // the what gets used is:
26739         // t.applySubtemplate(NAME, data, wrapping data..)
26740         // the outer template then get' applied with
26741         //     the store 'extra data'
26742         // and the body get's added to the
26743         //      roo-name="data" node?
26744         //      <span class='roo-tpl-{name}'></span> ?????
26745         
26746         
26747         
26748         this.clearSelections();
26749         this.el.update("");
26750         var html = [];
26751         var records = this.store.getRange();
26752         if(records.length < 1) {
26753             
26754             // is this valid??  = should it render a template??
26755             
26756             this.el.update(this.emptyText);
26757             return;
26758         }
26759         var el = this.el;
26760         if (this.dataName) {
26761             this.el.update(t.apply(this.store.meta)); //????
26762             el = this.el.child('.roo-tpl-' + this.dataName);
26763         }
26764         
26765         for(var i = 0, len = records.length; i < len; i++){
26766             var data = this.prepareData(records[i].data, i, records[i]);
26767             this.fireEvent("preparedata", this, data, i, records[i]);
26768             
26769             var d = Roo.apply({}, data);
26770             
26771             if(this.tickable){
26772                 Roo.apply(d, {'roo-id' : Roo.id()});
26773                 
26774                 var _this = this;
26775             
26776                 Roo.each(this.parent.item, function(item){
26777                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26778                         return;
26779                     }
26780                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26781                 });
26782             }
26783             
26784             html[html.length] = Roo.util.Format.trim(
26785                 this.dataName ?
26786                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26787                     t.apply(d)
26788             );
26789         }
26790         
26791         
26792         
26793         el.update(html.join(""));
26794         this.nodes = el.dom.childNodes;
26795         this.updateIndexes(0);
26796     },
26797     
26798
26799     /**
26800      * Function to override to reformat the data that is sent to
26801      * the template for each node.
26802      * DEPRICATED - use the preparedata event handler.
26803      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26804      * a JSON object for an UpdateManager bound view).
26805      */
26806     prepareData : function(data, index, record)
26807     {
26808         this.fireEvent("preparedata", this, data, index, record);
26809         return data;
26810     },
26811
26812     onUpdate : function(ds, record){
26813         // Roo.log('on update');   
26814         this.clearSelections();
26815         var index = this.store.indexOf(record);
26816         var n = this.nodes[index];
26817         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26818         n.parentNode.removeChild(n);
26819         this.updateIndexes(index, index);
26820     },
26821
26822     
26823     
26824 // --------- FIXME     
26825     onAdd : function(ds, records, index)
26826     {
26827         //Roo.log(['on Add', ds, records, index] );        
26828         this.clearSelections();
26829         if(this.nodes.length == 0){
26830             this.refresh();
26831             return;
26832         }
26833         var n = this.nodes[index];
26834         for(var i = 0, len = records.length; i < len; i++){
26835             var d = this.prepareData(records[i].data, i, records[i]);
26836             if(n){
26837                 this.tpl.insertBefore(n, d);
26838             }else{
26839                 
26840                 this.tpl.append(this.el, d);
26841             }
26842         }
26843         this.updateIndexes(index);
26844     },
26845
26846     onRemove : function(ds, record, index){
26847        // Roo.log('onRemove');
26848         this.clearSelections();
26849         var el = this.dataName  ?
26850             this.el.child('.roo-tpl-' + this.dataName) :
26851             this.el; 
26852         
26853         el.dom.removeChild(this.nodes[index]);
26854         this.updateIndexes(index);
26855     },
26856
26857     /**
26858      * Refresh an individual node.
26859      * @param {Number} index
26860      */
26861     refreshNode : function(index){
26862         this.onUpdate(this.store, this.store.getAt(index));
26863     },
26864
26865     updateIndexes : function(startIndex, endIndex){
26866         var ns = this.nodes;
26867         startIndex = startIndex || 0;
26868         endIndex = endIndex || ns.length - 1;
26869         for(var i = startIndex; i <= endIndex; i++){
26870             ns[i].nodeIndex = i;
26871         }
26872     },
26873
26874     /**
26875      * Changes the data store this view uses and refresh the view.
26876      * @param {Store} store
26877      */
26878     setStore : function(store, initial){
26879         if(!initial && this.store){
26880             this.store.un("datachanged", this.refresh);
26881             this.store.un("add", this.onAdd);
26882             this.store.un("remove", this.onRemove);
26883             this.store.un("update", this.onUpdate);
26884             this.store.un("clear", this.refresh);
26885             this.store.un("beforeload", this.onBeforeLoad);
26886             this.store.un("load", this.onLoad);
26887             this.store.un("loadexception", this.onLoad);
26888         }
26889         if(store){
26890           
26891             store.on("datachanged", this.refresh, this);
26892             store.on("add", this.onAdd, this);
26893             store.on("remove", this.onRemove, this);
26894             store.on("update", this.onUpdate, this);
26895             store.on("clear", this.refresh, this);
26896             store.on("beforeload", this.onBeforeLoad, this);
26897             store.on("load", this.onLoad, this);
26898             store.on("loadexception", this.onLoad, this);
26899         }
26900         
26901         if(store){
26902             this.refresh();
26903         }
26904     },
26905     /**
26906      * onbeforeLoad - masks the loading area.
26907      *
26908      */
26909     onBeforeLoad : function(store,opts)
26910     {
26911          //Roo.log('onBeforeLoad');   
26912         if (!opts.add) {
26913             this.el.update("");
26914         }
26915         this.el.mask(this.mask ? this.mask : "Loading" ); 
26916     },
26917     onLoad : function ()
26918     {
26919         this.el.unmask();
26920     },
26921     
26922
26923     /**
26924      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26925      * @param {HTMLElement} node
26926      * @return {HTMLElement} The template node
26927      */
26928     findItemFromChild : function(node){
26929         var el = this.dataName  ?
26930             this.el.child('.roo-tpl-' + this.dataName,true) :
26931             this.el.dom; 
26932         
26933         if(!node || node.parentNode == el){
26934                     return node;
26935             }
26936             var p = node.parentNode;
26937             while(p && p != el){
26938             if(p.parentNode == el){
26939                 return p;
26940             }
26941             p = p.parentNode;
26942         }
26943             return null;
26944     },
26945
26946     /** @ignore */
26947     onClick : function(e){
26948         var item = this.findItemFromChild(e.getTarget());
26949         if(item){
26950             var index = this.indexOf(item);
26951             if(this.onItemClick(item, index, e) !== false){
26952                 this.fireEvent("click", this, index, item, e);
26953             }
26954         }else{
26955             this.clearSelections();
26956         }
26957     },
26958
26959     /** @ignore */
26960     onContextMenu : function(e){
26961         var item = this.findItemFromChild(e.getTarget());
26962         if(item){
26963             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26964         }
26965     },
26966
26967     /** @ignore */
26968     onDblClick : function(e){
26969         var item = this.findItemFromChild(e.getTarget());
26970         if(item){
26971             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26972         }
26973     },
26974
26975     onItemClick : function(item, index, e)
26976     {
26977         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26978             return false;
26979         }
26980         if (this.toggleSelect) {
26981             var m = this.isSelected(item) ? 'unselect' : 'select';
26982             //Roo.log(m);
26983             var _t = this;
26984             _t[m](item, true, false);
26985             return true;
26986         }
26987         if(this.multiSelect || this.singleSelect){
26988             if(this.multiSelect && e.shiftKey && this.lastSelection){
26989                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26990             }else{
26991                 this.select(item, this.multiSelect && e.ctrlKey);
26992                 this.lastSelection = item;
26993             }
26994             
26995             if(!this.tickable){
26996                 e.preventDefault();
26997             }
26998             
26999         }
27000         return true;
27001     },
27002
27003     /**
27004      * Get the number of selected nodes.
27005      * @return {Number}
27006      */
27007     getSelectionCount : function(){
27008         return this.selections.length;
27009     },
27010
27011     /**
27012      * Get the currently selected nodes.
27013      * @return {Array} An array of HTMLElements
27014      */
27015     getSelectedNodes : function(){
27016         return this.selections;
27017     },
27018
27019     /**
27020      * Get the indexes of the selected nodes.
27021      * @return {Array}
27022      */
27023     getSelectedIndexes : function(){
27024         var indexes = [], s = this.selections;
27025         for(var i = 0, len = s.length; i < len; i++){
27026             indexes.push(s[i].nodeIndex);
27027         }
27028         return indexes;
27029     },
27030
27031     /**
27032      * Clear all selections
27033      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27034      */
27035     clearSelections : function(suppressEvent){
27036         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27037             this.cmp.elements = this.selections;
27038             this.cmp.removeClass(this.selectedClass);
27039             this.selections = [];
27040             if(!suppressEvent){
27041                 this.fireEvent("selectionchange", this, this.selections);
27042             }
27043         }
27044     },
27045
27046     /**
27047      * Returns true if the passed node is selected
27048      * @param {HTMLElement/Number} node The node or node index
27049      * @return {Boolean}
27050      */
27051     isSelected : function(node){
27052         var s = this.selections;
27053         if(s.length < 1){
27054             return false;
27055         }
27056         node = this.getNode(node);
27057         return s.indexOf(node) !== -1;
27058     },
27059
27060     /**
27061      * Selects nodes.
27062      * @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
27063      * @param {Boolean} keepExisting (optional) true to keep existing selections
27064      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27065      */
27066     select : function(nodeInfo, keepExisting, suppressEvent){
27067         if(nodeInfo instanceof Array){
27068             if(!keepExisting){
27069                 this.clearSelections(true);
27070             }
27071             for(var i = 0, len = nodeInfo.length; i < len; i++){
27072                 this.select(nodeInfo[i], true, true);
27073             }
27074             return;
27075         } 
27076         var node = this.getNode(nodeInfo);
27077         if(!node || this.isSelected(node)){
27078             return; // already selected.
27079         }
27080         if(!keepExisting){
27081             this.clearSelections(true);
27082         }
27083         
27084         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27085             Roo.fly(node).addClass(this.selectedClass);
27086             this.selections.push(node);
27087             if(!suppressEvent){
27088                 this.fireEvent("selectionchange", this, this.selections);
27089             }
27090         }
27091         
27092         
27093     },
27094       /**
27095      * Unselects nodes.
27096      * @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
27097      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27098      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27099      */
27100     unselect : function(nodeInfo, keepExisting, suppressEvent)
27101     {
27102         if(nodeInfo instanceof Array){
27103             Roo.each(this.selections, function(s) {
27104                 this.unselect(s, nodeInfo);
27105             }, this);
27106             return;
27107         }
27108         var node = this.getNode(nodeInfo);
27109         if(!node || !this.isSelected(node)){
27110             //Roo.log("not selected");
27111             return; // not selected.
27112         }
27113         // fireevent???
27114         var ns = [];
27115         Roo.each(this.selections, function(s) {
27116             if (s == node ) {
27117                 Roo.fly(node).removeClass(this.selectedClass);
27118
27119                 return;
27120             }
27121             ns.push(s);
27122         },this);
27123         
27124         this.selections= ns;
27125         this.fireEvent("selectionchange", this, this.selections);
27126     },
27127
27128     /**
27129      * Gets a template node.
27130      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27131      * @return {HTMLElement} The node or null if it wasn't found
27132      */
27133     getNode : function(nodeInfo){
27134         if(typeof nodeInfo == "string"){
27135             return document.getElementById(nodeInfo);
27136         }else if(typeof nodeInfo == "number"){
27137             return this.nodes[nodeInfo];
27138         }
27139         return nodeInfo;
27140     },
27141
27142     /**
27143      * Gets a range template nodes.
27144      * @param {Number} startIndex
27145      * @param {Number} endIndex
27146      * @return {Array} An array of nodes
27147      */
27148     getNodes : function(start, end){
27149         var ns = this.nodes;
27150         start = start || 0;
27151         end = typeof end == "undefined" ? ns.length - 1 : end;
27152         var nodes = [];
27153         if(start <= end){
27154             for(var i = start; i <= end; i++){
27155                 nodes.push(ns[i]);
27156             }
27157         } else{
27158             for(var i = start; i >= end; i--){
27159                 nodes.push(ns[i]);
27160             }
27161         }
27162         return nodes;
27163     },
27164
27165     /**
27166      * Finds the index of the passed node
27167      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27168      * @return {Number} The index of the node or -1
27169      */
27170     indexOf : function(node){
27171         node = this.getNode(node);
27172         if(typeof node.nodeIndex == "number"){
27173             return node.nodeIndex;
27174         }
27175         var ns = this.nodes;
27176         for(var i = 0, len = ns.length; i < len; i++){
27177             if(ns[i] == node){
27178                 return i;
27179             }
27180         }
27181         return -1;
27182     }
27183 });
27184 /*
27185  * Based on:
27186  * Ext JS Library 1.1.1
27187  * Copyright(c) 2006-2007, Ext JS, LLC.
27188  *
27189  * Originally Released Under LGPL - original licence link has changed is not relivant.
27190  *
27191  * Fork - LGPL
27192  * <script type="text/javascript">
27193  */
27194
27195 /**
27196  * @class Roo.JsonView
27197  * @extends Roo.View
27198  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27199 <pre><code>
27200 var view = new Roo.JsonView({
27201     container: "my-element",
27202     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27203     multiSelect: true, 
27204     jsonRoot: "data" 
27205 });
27206
27207 // listen for node click?
27208 view.on("click", function(vw, index, node, e){
27209     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27210 });
27211
27212 // direct load of JSON data
27213 view.load("foobar.php");
27214
27215 // Example from my blog list
27216 var tpl = new Roo.Template(
27217     '&lt;div class="entry"&gt;' +
27218     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27219     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27220     "&lt;/div&gt;&lt;hr /&gt;"
27221 );
27222
27223 var moreView = new Roo.JsonView({
27224     container :  "entry-list", 
27225     template : tpl,
27226     jsonRoot: "posts"
27227 });
27228 moreView.on("beforerender", this.sortEntries, this);
27229 moreView.load({
27230     url: "/blog/get-posts.php",
27231     params: "allposts=true",
27232     text: "Loading Blog Entries..."
27233 });
27234 </code></pre>
27235
27236 * Note: old code is supported with arguments : (container, template, config)
27237
27238
27239  * @constructor
27240  * Create a new JsonView
27241  * 
27242  * @param {Object} config The config object
27243  * 
27244  */
27245 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27246     
27247     
27248     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27249
27250     var um = this.el.getUpdateManager();
27251     um.setRenderer(this);
27252     um.on("update", this.onLoad, this);
27253     um.on("failure", this.onLoadException, this);
27254
27255     /**
27256      * @event beforerender
27257      * Fires before rendering of the downloaded JSON data.
27258      * @param {Roo.JsonView} this
27259      * @param {Object} data The JSON data loaded
27260      */
27261     /**
27262      * @event load
27263      * Fires when data is loaded.
27264      * @param {Roo.JsonView} this
27265      * @param {Object} data The JSON data loaded
27266      * @param {Object} response The raw Connect response object
27267      */
27268     /**
27269      * @event loadexception
27270      * Fires when loading fails.
27271      * @param {Roo.JsonView} this
27272      * @param {Object} response The raw Connect response object
27273      */
27274     this.addEvents({
27275         'beforerender' : true,
27276         'load' : true,
27277         'loadexception' : true
27278     });
27279 };
27280 Roo.extend(Roo.JsonView, Roo.View, {
27281     /**
27282      * @type {String} The root property in the loaded JSON object that contains the data
27283      */
27284     jsonRoot : "",
27285
27286     /**
27287      * Refreshes the view.
27288      */
27289     refresh : function(){
27290         this.clearSelections();
27291         this.el.update("");
27292         var html = [];
27293         var o = this.jsonData;
27294         if(o && o.length > 0){
27295             for(var i = 0, len = o.length; i < len; i++){
27296                 var data = this.prepareData(o[i], i, o);
27297                 html[html.length] = this.tpl.apply(data);
27298             }
27299         }else{
27300             html.push(this.emptyText);
27301         }
27302         this.el.update(html.join(""));
27303         this.nodes = this.el.dom.childNodes;
27304         this.updateIndexes(0);
27305     },
27306
27307     /**
27308      * 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.
27309      * @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:
27310      <pre><code>
27311      view.load({
27312          url: "your-url.php",
27313          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27314          callback: yourFunction,
27315          scope: yourObject, //(optional scope)
27316          discardUrl: false,
27317          nocache: false,
27318          text: "Loading...",
27319          timeout: 30,
27320          scripts: false
27321      });
27322      </code></pre>
27323      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27324      * 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.
27325      * @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}
27326      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27327      * @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.
27328      */
27329     load : function(){
27330         var um = this.el.getUpdateManager();
27331         um.update.apply(um, arguments);
27332     },
27333
27334     // note - render is a standard framework call...
27335     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27336     render : function(el, response){
27337         
27338         this.clearSelections();
27339         this.el.update("");
27340         var o;
27341         try{
27342             if (response != '') {
27343                 o = Roo.util.JSON.decode(response.responseText);
27344                 if(this.jsonRoot){
27345                     
27346                     o = o[this.jsonRoot];
27347                 }
27348             }
27349         } catch(e){
27350         }
27351         /**
27352          * The current JSON data or null
27353          */
27354         this.jsonData = o;
27355         this.beforeRender();
27356         this.refresh();
27357     },
27358
27359 /**
27360  * Get the number of records in the current JSON dataset
27361  * @return {Number}
27362  */
27363     getCount : function(){
27364         return this.jsonData ? this.jsonData.length : 0;
27365     },
27366
27367 /**
27368  * Returns the JSON object for the specified node(s)
27369  * @param {HTMLElement/Array} node The node or an array of nodes
27370  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27371  * you get the JSON object for the node
27372  */
27373     getNodeData : function(node){
27374         if(node instanceof Array){
27375             var data = [];
27376             for(var i = 0, len = node.length; i < len; i++){
27377                 data.push(this.getNodeData(node[i]));
27378             }
27379             return data;
27380         }
27381         return this.jsonData[this.indexOf(node)] || null;
27382     },
27383
27384     beforeRender : function(){
27385         this.snapshot = this.jsonData;
27386         if(this.sortInfo){
27387             this.sort.apply(this, this.sortInfo);
27388         }
27389         this.fireEvent("beforerender", this, this.jsonData);
27390     },
27391
27392     onLoad : function(el, o){
27393         this.fireEvent("load", this, this.jsonData, o);
27394     },
27395
27396     onLoadException : function(el, o){
27397         this.fireEvent("loadexception", this, o);
27398     },
27399
27400 /**
27401  * Filter the data by a specific property.
27402  * @param {String} property A property on your JSON objects
27403  * @param {String/RegExp} value Either string that the property values
27404  * should start with, or a RegExp to test against the property
27405  */
27406     filter : function(property, value){
27407         if(this.jsonData){
27408             var data = [];
27409             var ss = this.snapshot;
27410             if(typeof value == "string"){
27411                 var vlen = value.length;
27412                 if(vlen == 0){
27413                     this.clearFilter();
27414                     return;
27415                 }
27416                 value = value.toLowerCase();
27417                 for(var i = 0, len = ss.length; i < len; i++){
27418                     var o = ss[i];
27419                     if(o[property].substr(0, vlen).toLowerCase() == value){
27420                         data.push(o);
27421                     }
27422                 }
27423             } else if(value.exec){ // regex?
27424                 for(var i = 0, len = ss.length; i < len; i++){
27425                     var o = ss[i];
27426                     if(value.test(o[property])){
27427                         data.push(o);
27428                     }
27429                 }
27430             } else{
27431                 return;
27432             }
27433             this.jsonData = data;
27434             this.refresh();
27435         }
27436     },
27437
27438 /**
27439  * Filter by a function. The passed function will be called with each
27440  * object in the current dataset. If the function returns true the value is kept,
27441  * otherwise it is filtered.
27442  * @param {Function} fn
27443  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27444  */
27445     filterBy : function(fn, scope){
27446         if(this.jsonData){
27447             var data = [];
27448             var ss = this.snapshot;
27449             for(var i = 0, len = ss.length; i < len; i++){
27450                 var o = ss[i];
27451                 if(fn.call(scope || this, o)){
27452                     data.push(o);
27453                 }
27454             }
27455             this.jsonData = data;
27456             this.refresh();
27457         }
27458     },
27459
27460 /**
27461  * Clears the current filter.
27462  */
27463     clearFilter : function(){
27464         if(this.snapshot && this.jsonData != this.snapshot){
27465             this.jsonData = this.snapshot;
27466             this.refresh();
27467         }
27468     },
27469
27470
27471 /**
27472  * Sorts the data for this view and refreshes it.
27473  * @param {String} property A property on your JSON objects to sort on
27474  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27475  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27476  */
27477     sort : function(property, dir, sortType){
27478         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27479         if(this.jsonData){
27480             var p = property;
27481             var dsc = dir && dir.toLowerCase() == "desc";
27482             var f = function(o1, o2){
27483                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27484                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27485                 ;
27486                 if(v1 < v2){
27487                     return dsc ? +1 : -1;
27488                 } else if(v1 > v2){
27489                     return dsc ? -1 : +1;
27490                 } else{
27491                     return 0;
27492                 }
27493             };
27494             this.jsonData.sort(f);
27495             this.refresh();
27496             if(this.jsonData != this.snapshot){
27497                 this.snapshot.sort(f);
27498             }
27499         }
27500     }
27501 });/*
27502  * Based on:
27503  * Ext JS Library 1.1.1
27504  * Copyright(c) 2006-2007, Ext JS, LLC.
27505  *
27506  * Originally Released Under LGPL - original licence link has changed is not relivant.
27507  *
27508  * Fork - LGPL
27509  * <script type="text/javascript">
27510  */
27511  
27512
27513 /**
27514  * @class Roo.ColorPalette
27515  * @extends Roo.Component
27516  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27517  * Here's an example of typical usage:
27518  * <pre><code>
27519 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27520 cp.render('my-div');
27521
27522 cp.on('select', function(palette, selColor){
27523     // do something with selColor
27524 });
27525 </code></pre>
27526  * @constructor
27527  * Create a new ColorPalette
27528  * @param {Object} config The config object
27529  */
27530 Roo.ColorPalette = function(config){
27531     Roo.ColorPalette.superclass.constructor.call(this, config);
27532     this.addEvents({
27533         /**
27534              * @event select
27535              * Fires when a color is selected
27536              * @param {ColorPalette} this
27537              * @param {String} color The 6-digit color hex code (without the # symbol)
27538              */
27539         select: true
27540     });
27541
27542     if(this.handler){
27543         this.on("select", this.handler, this.scope, true);
27544     }
27545 };
27546 Roo.extend(Roo.ColorPalette, Roo.Component, {
27547     /**
27548      * @cfg {String} itemCls
27549      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27550      */
27551     itemCls : "x-color-palette",
27552     /**
27553      * @cfg {String} value
27554      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27555      * the hex codes are case-sensitive.
27556      */
27557     value : null,
27558     clickEvent:'click',
27559     // private
27560     ctype: "Roo.ColorPalette",
27561
27562     /**
27563      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27564      */
27565     allowReselect : false,
27566
27567     /**
27568      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27569      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27570      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27571      * of colors with the width setting until the box is symmetrical.</p>
27572      * <p>You can override individual colors if needed:</p>
27573      * <pre><code>
27574 var cp = new Roo.ColorPalette();
27575 cp.colors[0] = "FF0000";  // change the first box to red
27576 </code></pre>
27577
27578 Or you can provide a custom array of your own for complete control:
27579 <pre><code>
27580 var cp = new Roo.ColorPalette();
27581 cp.colors = ["000000", "993300", "333300"];
27582 </code></pre>
27583      * @type Array
27584      */
27585     colors : [
27586         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27587         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27588         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27589         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27590         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27591     ],
27592
27593     // private
27594     onRender : function(container, position){
27595         var t = new Roo.MasterTemplate(
27596             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27597         );
27598         var c = this.colors;
27599         for(var i = 0, len = c.length; i < len; i++){
27600             t.add([c[i]]);
27601         }
27602         var el = document.createElement("div");
27603         el.className = this.itemCls;
27604         t.overwrite(el);
27605         container.dom.insertBefore(el, position);
27606         this.el = Roo.get(el);
27607         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27608         if(this.clickEvent != 'click'){
27609             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27610         }
27611     },
27612
27613     // private
27614     afterRender : function(){
27615         Roo.ColorPalette.superclass.afterRender.call(this);
27616         if(this.value){
27617             var s = this.value;
27618             this.value = null;
27619             this.select(s);
27620         }
27621     },
27622
27623     // private
27624     handleClick : function(e, t){
27625         e.preventDefault();
27626         if(!this.disabled){
27627             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27628             this.select(c.toUpperCase());
27629         }
27630     },
27631
27632     /**
27633      * Selects the specified color in the palette (fires the select event)
27634      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27635      */
27636     select : function(color){
27637         color = color.replace("#", "");
27638         if(color != this.value || this.allowReselect){
27639             var el = this.el;
27640             if(this.value){
27641                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27642             }
27643             el.child("a.color-"+color).addClass("x-color-palette-sel");
27644             this.value = color;
27645             this.fireEvent("select", this, color);
27646         }
27647     }
27648 });/*
27649  * Based on:
27650  * Ext JS Library 1.1.1
27651  * Copyright(c) 2006-2007, Ext JS, LLC.
27652  *
27653  * Originally Released Under LGPL - original licence link has changed is not relivant.
27654  *
27655  * Fork - LGPL
27656  * <script type="text/javascript">
27657  */
27658  
27659 /**
27660  * @class Roo.DatePicker
27661  * @extends Roo.Component
27662  * Simple date picker class.
27663  * @constructor
27664  * Create a new DatePicker
27665  * @param {Object} config The config object
27666  */
27667 Roo.DatePicker = function(config){
27668     Roo.DatePicker.superclass.constructor.call(this, config);
27669
27670     this.value = config && config.value ?
27671                  config.value.clearTime() : new Date().clearTime();
27672
27673     this.addEvents({
27674         /**
27675              * @event select
27676              * Fires when a date is selected
27677              * @param {DatePicker} this
27678              * @param {Date} date The selected date
27679              */
27680         'select': true,
27681         /**
27682              * @event monthchange
27683              * Fires when the displayed month changes 
27684              * @param {DatePicker} this
27685              * @param {Date} date The selected month
27686              */
27687         'monthchange': true
27688     });
27689
27690     if(this.handler){
27691         this.on("select", this.handler,  this.scope || this);
27692     }
27693     // build the disabledDatesRE
27694     if(!this.disabledDatesRE && this.disabledDates){
27695         var dd = this.disabledDates;
27696         var re = "(?:";
27697         for(var i = 0; i < dd.length; i++){
27698             re += dd[i];
27699             if(i != dd.length-1) {
27700                 re += "|";
27701             }
27702         }
27703         this.disabledDatesRE = new RegExp(re + ")");
27704     }
27705 };
27706
27707 Roo.extend(Roo.DatePicker, Roo.Component, {
27708     /**
27709      * @cfg {String} todayText
27710      * The text to display on the button that selects the current date (defaults to "Today")
27711      */
27712     todayText : "Today",
27713     /**
27714      * @cfg {String} okText
27715      * The text to display on the ok button
27716      */
27717     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27718     /**
27719      * @cfg {String} cancelText
27720      * The text to display on the cancel button
27721      */
27722     cancelText : "Cancel",
27723     /**
27724      * @cfg {String} todayTip
27725      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27726      */
27727     todayTip : "{0} (Spacebar)",
27728     /**
27729      * @cfg {Date} minDate
27730      * Minimum allowable date (JavaScript date object, defaults to null)
27731      */
27732     minDate : null,
27733     /**
27734      * @cfg {Date} maxDate
27735      * Maximum allowable date (JavaScript date object, defaults to null)
27736      */
27737     maxDate : null,
27738     /**
27739      * @cfg {String} minText
27740      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27741      */
27742     minText : "This date is before the minimum date",
27743     /**
27744      * @cfg {String} maxText
27745      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27746      */
27747     maxText : "This date is after the maximum date",
27748     /**
27749      * @cfg {String} format
27750      * The default date format string which can be overriden for localization support.  The format must be
27751      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27752      */
27753     format : "m/d/y",
27754     /**
27755      * @cfg {Array} disabledDays
27756      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27757      */
27758     disabledDays : null,
27759     /**
27760      * @cfg {String} disabledDaysText
27761      * The tooltip to display when the date falls on a disabled day (defaults to "")
27762      */
27763     disabledDaysText : "",
27764     /**
27765      * @cfg {RegExp} disabledDatesRE
27766      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27767      */
27768     disabledDatesRE : null,
27769     /**
27770      * @cfg {String} disabledDatesText
27771      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27772      */
27773     disabledDatesText : "",
27774     /**
27775      * @cfg {Boolean} constrainToViewport
27776      * True to constrain the date picker to the viewport (defaults to true)
27777      */
27778     constrainToViewport : true,
27779     /**
27780      * @cfg {Array} monthNames
27781      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27782      */
27783     monthNames : Date.monthNames,
27784     /**
27785      * @cfg {Array} dayNames
27786      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27787      */
27788     dayNames : Date.dayNames,
27789     /**
27790      * @cfg {String} nextText
27791      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27792      */
27793     nextText: 'Next Month (Control+Right)',
27794     /**
27795      * @cfg {String} prevText
27796      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27797      */
27798     prevText: 'Previous Month (Control+Left)',
27799     /**
27800      * @cfg {String} monthYearText
27801      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27802      */
27803     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27804     /**
27805      * @cfg {Number} startDay
27806      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27807      */
27808     startDay : 0,
27809     /**
27810      * @cfg {Bool} showClear
27811      * Show a clear button (usefull for date form elements that can be blank.)
27812      */
27813     
27814     showClear: false,
27815     
27816     /**
27817      * Sets the value of the date field
27818      * @param {Date} value The date to set
27819      */
27820     setValue : function(value){
27821         var old = this.value;
27822         
27823         if (typeof(value) == 'string') {
27824          
27825             value = Date.parseDate(value, this.format);
27826         }
27827         if (!value) {
27828             value = new Date();
27829         }
27830         
27831         this.value = value.clearTime(true);
27832         if(this.el){
27833             this.update(this.value);
27834         }
27835     },
27836
27837     /**
27838      * Gets the current selected value of the date field
27839      * @return {Date} The selected date
27840      */
27841     getValue : function(){
27842         return this.value;
27843     },
27844
27845     // private
27846     focus : function(){
27847         if(this.el){
27848             this.update(this.activeDate);
27849         }
27850     },
27851
27852     // privateval
27853     onRender : function(container, position){
27854         
27855         var m = [
27856              '<table cellspacing="0">',
27857                 '<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>',
27858                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27859         var dn = this.dayNames;
27860         for(var i = 0; i < 7; i++){
27861             var d = this.startDay+i;
27862             if(d > 6){
27863                 d = d-7;
27864             }
27865             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27866         }
27867         m[m.length] = "</tr></thead><tbody><tr>";
27868         for(var i = 0; i < 42; i++) {
27869             if(i % 7 == 0 && i != 0){
27870                 m[m.length] = "</tr><tr>";
27871             }
27872             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27873         }
27874         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27875             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27876
27877         var el = document.createElement("div");
27878         el.className = "x-date-picker";
27879         el.innerHTML = m.join("");
27880
27881         container.dom.insertBefore(el, position);
27882
27883         this.el = Roo.get(el);
27884         this.eventEl = Roo.get(el.firstChild);
27885
27886         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27887             handler: this.showPrevMonth,
27888             scope: this,
27889             preventDefault:true,
27890             stopDefault:true
27891         });
27892
27893         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27894             handler: this.showNextMonth,
27895             scope: this,
27896             preventDefault:true,
27897             stopDefault:true
27898         });
27899
27900         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27901
27902         this.monthPicker = this.el.down('div.x-date-mp');
27903         this.monthPicker.enableDisplayMode('block');
27904         
27905         var kn = new Roo.KeyNav(this.eventEl, {
27906             "left" : function(e){
27907                 e.ctrlKey ?
27908                     this.showPrevMonth() :
27909                     this.update(this.activeDate.add("d", -1));
27910             },
27911
27912             "right" : function(e){
27913                 e.ctrlKey ?
27914                     this.showNextMonth() :
27915                     this.update(this.activeDate.add("d", 1));
27916             },
27917
27918             "up" : function(e){
27919                 e.ctrlKey ?
27920                     this.showNextYear() :
27921                     this.update(this.activeDate.add("d", -7));
27922             },
27923
27924             "down" : function(e){
27925                 e.ctrlKey ?
27926                     this.showPrevYear() :
27927                     this.update(this.activeDate.add("d", 7));
27928             },
27929
27930             "pageUp" : function(e){
27931                 this.showNextMonth();
27932             },
27933
27934             "pageDown" : function(e){
27935                 this.showPrevMonth();
27936             },
27937
27938             "enter" : function(e){
27939                 e.stopPropagation();
27940                 return true;
27941             },
27942
27943             scope : this
27944         });
27945
27946         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27947
27948         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27949
27950         this.el.unselectable();
27951         
27952         this.cells = this.el.select("table.x-date-inner tbody td");
27953         this.textNodes = this.el.query("table.x-date-inner tbody span");
27954
27955         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27956             text: "&#160;",
27957             tooltip: this.monthYearText
27958         });
27959
27960         this.mbtn.on('click', this.showMonthPicker, this);
27961         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27962
27963
27964         var today = (new Date()).dateFormat(this.format);
27965         
27966         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27967         if (this.showClear) {
27968             baseTb.add( new Roo.Toolbar.Fill());
27969         }
27970         baseTb.add({
27971             text: String.format(this.todayText, today),
27972             tooltip: String.format(this.todayTip, today),
27973             handler: this.selectToday,
27974             scope: this
27975         });
27976         
27977         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27978             
27979         //});
27980         if (this.showClear) {
27981             
27982             baseTb.add( new Roo.Toolbar.Fill());
27983             baseTb.add({
27984                 text: '&#160;',
27985                 cls: 'x-btn-icon x-btn-clear',
27986                 handler: function() {
27987                     //this.value = '';
27988                     this.fireEvent("select", this, '');
27989                 },
27990                 scope: this
27991             });
27992         }
27993         
27994         
27995         if(Roo.isIE){
27996             this.el.repaint();
27997         }
27998         this.update(this.value);
27999     },
28000
28001     createMonthPicker : function(){
28002         if(!this.monthPicker.dom.firstChild){
28003             var buf = ['<table border="0" cellspacing="0">'];
28004             for(var i = 0; i < 6; i++){
28005                 buf.push(
28006                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28007                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28008                     i == 0 ?
28009                     '<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>' :
28010                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28011                 );
28012             }
28013             buf.push(
28014                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28015                     this.okText,
28016                     '</button><button type="button" class="x-date-mp-cancel">',
28017                     this.cancelText,
28018                     '</button></td></tr>',
28019                 '</table>'
28020             );
28021             this.monthPicker.update(buf.join(''));
28022             this.monthPicker.on('click', this.onMonthClick, this);
28023             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28024
28025             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28026             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28027
28028             this.mpMonths.each(function(m, a, i){
28029                 i += 1;
28030                 if((i%2) == 0){
28031                     m.dom.xmonth = 5 + Math.round(i * .5);
28032                 }else{
28033                     m.dom.xmonth = Math.round((i-1) * .5);
28034                 }
28035             });
28036         }
28037     },
28038
28039     showMonthPicker : function(){
28040         this.createMonthPicker();
28041         var size = this.el.getSize();
28042         this.monthPicker.setSize(size);
28043         this.monthPicker.child('table').setSize(size);
28044
28045         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28046         this.updateMPMonth(this.mpSelMonth);
28047         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28048         this.updateMPYear(this.mpSelYear);
28049
28050         this.monthPicker.slideIn('t', {duration:.2});
28051     },
28052
28053     updateMPYear : function(y){
28054         this.mpyear = y;
28055         var ys = this.mpYears.elements;
28056         for(var i = 1; i <= 10; i++){
28057             var td = ys[i-1], y2;
28058             if((i%2) == 0){
28059                 y2 = y + Math.round(i * .5);
28060                 td.firstChild.innerHTML = y2;
28061                 td.xyear = y2;
28062             }else{
28063                 y2 = y - (5-Math.round(i * .5));
28064                 td.firstChild.innerHTML = y2;
28065                 td.xyear = y2;
28066             }
28067             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28068         }
28069     },
28070
28071     updateMPMonth : function(sm){
28072         this.mpMonths.each(function(m, a, i){
28073             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28074         });
28075     },
28076
28077     selectMPMonth: function(m){
28078         
28079     },
28080
28081     onMonthClick : function(e, t){
28082         e.stopEvent();
28083         var el = new Roo.Element(t), pn;
28084         if(el.is('button.x-date-mp-cancel')){
28085             this.hideMonthPicker();
28086         }
28087         else if(el.is('button.x-date-mp-ok')){
28088             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28089             this.hideMonthPicker();
28090         }
28091         else if(pn = el.up('td.x-date-mp-month', 2)){
28092             this.mpMonths.removeClass('x-date-mp-sel');
28093             pn.addClass('x-date-mp-sel');
28094             this.mpSelMonth = pn.dom.xmonth;
28095         }
28096         else if(pn = el.up('td.x-date-mp-year', 2)){
28097             this.mpYears.removeClass('x-date-mp-sel');
28098             pn.addClass('x-date-mp-sel');
28099             this.mpSelYear = pn.dom.xyear;
28100         }
28101         else if(el.is('a.x-date-mp-prev')){
28102             this.updateMPYear(this.mpyear-10);
28103         }
28104         else if(el.is('a.x-date-mp-next')){
28105             this.updateMPYear(this.mpyear+10);
28106         }
28107     },
28108
28109     onMonthDblClick : function(e, t){
28110         e.stopEvent();
28111         var el = new Roo.Element(t), pn;
28112         if(pn = el.up('td.x-date-mp-month', 2)){
28113             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28114             this.hideMonthPicker();
28115         }
28116         else if(pn = el.up('td.x-date-mp-year', 2)){
28117             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28118             this.hideMonthPicker();
28119         }
28120     },
28121
28122     hideMonthPicker : function(disableAnim){
28123         if(this.monthPicker){
28124             if(disableAnim === true){
28125                 this.monthPicker.hide();
28126             }else{
28127                 this.monthPicker.slideOut('t', {duration:.2});
28128             }
28129         }
28130     },
28131
28132     // private
28133     showPrevMonth : function(e){
28134         this.update(this.activeDate.add("mo", -1));
28135     },
28136
28137     // private
28138     showNextMonth : function(e){
28139         this.update(this.activeDate.add("mo", 1));
28140     },
28141
28142     // private
28143     showPrevYear : function(){
28144         this.update(this.activeDate.add("y", -1));
28145     },
28146
28147     // private
28148     showNextYear : function(){
28149         this.update(this.activeDate.add("y", 1));
28150     },
28151
28152     // private
28153     handleMouseWheel : function(e){
28154         var delta = e.getWheelDelta();
28155         if(delta > 0){
28156             this.showPrevMonth();
28157             e.stopEvent();
28158         } else if(delta < 0){
28159             this.showNextMonth();
28160             e.stopEvent();
28161         }
28162     },
28163
28164     // private
28165     handleDateClick : function(e, t){
28166         e.stopEvent();
28167         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28168             this.setValue(new Date(t.dateValue));
28169             this.fireEvent("select", this, this.value);
28170         }
28171     },
28172
28173     // private
28174     selectToday : function(){
28175         this.setValue(new Date().clearTime());
28176         this.fireEvent("select", this, this.value);
28177     },
28178
28179     // private
28180     update : function(date)
28181     {
28182         var vd = this.activeDate;
28183         this.activeDate = date;
28184         if(vd && this.el){
28185             var t = date.getTime();
28186             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28187                 this.cells.removeClass("x-date-selected");
28188                 this.cells.each(function(c){
28189                    if(c.dom.firstChild.dateValue == t){
28190                        c.addClass("x-date-selected");
28191                        setTimeout(function(){
28192                             try{c.dom.firstChild.focus();}catch(e){}
28193                        }, 50);
28194                        return false;
28195                    }
28196                 });
28197                 return;
28198             }
28199         }
28200         
28201         var days = date.getDaysInMonth();
28202         var firstOfMonth = date.getFirstDateOfMonth();
28203         var startingPos = firstOfMonth.getDay()-this.startDay;
28204
28205         if(startingPos <= this.startDay){
28206             startingPos += 7;
28207         }
28208
28209         var pm = date.add("mo", -1);
28210         var prevStart = pm.getDaysInMonth()-startingPos;
28211
28212         var cells = this.cells.elements;
28213         var textEls = this.textNodes;
28214         days += startingPos;
28215
28216         // convert everything to numbers so it's fast
28217         var day = 86400000;
28218         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28219         var today = new Date().clearTime().getTime();
28220         var sel = date.clearTime().getTime();
28221         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28222         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28223         var ddMatch = this.disabledDatesRE;
28224         var ddText = this.disabledDatesText;
28225         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28226         var ddaysText = this.disabledDaysText;
28227         var format = this.format;
28228
28229         var setCellClass = function(cal, cell){
28230             cell.title = "";
28231             var t = d.getTime();
28232             cell.firstChild.dateValue = t;
28233             if(t == today){
28234                 cell.className += " x-date-today";
28235                 cell.title = cal.todayText;
28236             }
28237             if(t == sel){
28238                 cell.className += " x-date-selected";
28239                 setTimeout(function(){
28240                     try{cell.firstChild.focus();}catch(e){}
28241                 }, 50);
28242             }
28243             // disabling
28244             if(t < min) {
28245                 cell.className = " x-date-disabled";
28246                 cell.title = cal.minText;
28247                 return;
28248             }
28249             if(t > max) {
28250                 cell.className = " x-date-disabled";
28251                 cell.title = cal.maxText;
28252                 return;
28253             }
28254             if(ddays){
28255                 if(ddays.indexOf(d.getDay()) != -1){
28256                     cell.title = ddaysText;
28257                     cell.className = " x-date-disabled";
28258                 }
28259             }
28260             if(ddMatch && format){
28261                 var fvalue = d.dateFormat(format);
28262                 if(ddMatch.test(fvalue)){
28263                     cell.title = ddText.replace("%0", fvalue);
28264                     cell.className = " x-date-disabled";
28265                 }
28266             }
28267         };
28268
28269         var i = 0;
28270         for(; i < startingPos; i++) {
28271             textEls[i].innerHTML = (++prevStart);
28272             d.setDate(d.getDate()+1);
28273             cells[i].className = "x-date-prevday";
28274             setCellClass(this, cells[i]);
28275         }
28276         for(; i < days; i++){
28277             intDay = i - startingPos + 1;
28278             textEls[i].innerHTML = (intDay);
28279             d.setDate(d.getDate()+1);
28280             cells[i].className = "x-date-active";
28281             setCellClass(this, cells[i]);
28282         }
28283         var extraDays = 0;
28284         for(; i < 42; i++) {
28285              textEls[i].innerHTML = (++extraDays);
28286              d.setDate(d.getDate()+1);
28287              cells[i].className = "x-date-nextday";
28288              setCellClass(this, cells[i]);
28289         }
28290
28291         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28292         this.fireEvent('monthchange', this, date);
28293         
28294         if(!this.internalRender){
28295             var main = this.el.dom.firstChild;
28296             var w = main.offsetWidth;
28297             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28298             Roo.fly(main).setWidth(w);
28299             this.internalRender = true;
28300             // opera does not respect the auto grow header center column
28301             // then, after it gets a width opera refuses to recalculate
28302             // without a second pass
28303             if(Roo.isOpera && !this.secondPass){
28304                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28305                 this.secondPass = true;
28306                 this.update.defer(10, this, [date]);
28307             }
28308         }
28309         
28310         
28311     }
28312 });        /*
28313  * Based on:
28314  * Ext JS Library 1.1.1
28315  * Copyright(c) 2006-2007, Ext JS, LLC.
28316  *
28317  * Originally Released Under LGPL - original licence link has changed is not relivant.
28318  *
28319  * Fork - LGPL
28320  * <script type="text/javascript">
28321  */
28322 /**
28323  * @class Roo.TabPanel
28324  * @extends Roo.util.Observable
28325  * A lightweight tab container.
28326  * <br><br>
28327  * Usage:
28328  * <pre><code>
28329 // basic tabs 1, built from existing content
28330 var tabs = new Roo.TabPanel("tabs1");
28331 tabs.addTab("script", "View Script");
28332 tabs.addTab("markup", "View Markup");
28333 tabs.activate("script");
28334
28335 // more advanced tabs, built from javascript
28336 var jtabs = new Roo.TabPanel("jtabs");
28337 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28338
28339 // set up the UpdateManager
28340 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28341 var updater = tab2.getUpdateManager();
28342 updater.setDefaultUrl("ajax1.htm");
28343 tab2.on('activate', updater.refresh, updater, true);
28344
28345 // Use setUrl for Ajax loading
28346 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28347 tab3.setUrl("ajax2.htm", null, true);
28348
28349 // Disabled tab
28350 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28351 tab4.disable();
28352
28353 jtabs.activate("jtabs-1");
28354  * </code></pre>
28355  * @constructor
28356  * Create a new TabPanel.
28357  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28358  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28359  */
28360 Roo.TabPanel = function(container, config){
28361     /**
28362     * The container element for this TabPanel.
28363     * @type Roo.Element
28364     */
28365     this.el = Roo.get(container, true);
28366     if(config){
28367         if(typeof config == "boolean"){
28368             this.tabPosition = config ? "bottom" : "top";
28369         }else{
28370             Roo.apply(this, config);
28371         }
28372     }
28373     if(this.tabPosition == "bottom"){
28374         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28375         this.el.addClass("x-tabs-bottom");
28376     }
28377     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28378     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28379     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28380     if(Roo.isIE){
28381         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28382     }
28383     if(this.tabPosition != "bottom"){
28384         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28385          * @type Roo.Element
28386          */
28387         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28388         this.el.addClass("x-tabs-top");
28389     }
28390     this.items = [];
28391
28392     this.bodyEl.setStyle("position", "relative");
28393
28394     this.active = null;
28395     this.activateDelegate = this.activate.createDelegate(this);
28396
28397     this.addEvents({
28398         /**
28399          * @event tabchange
28400          * Fires when the active tab changes
28401          * @param {Roo.TabPanel} this
28402          * @param {Roo.TabPanelItem} activePanel The new active tab
28403          */
28404         "tabchange": true,
28405         /**
28406          * @event beforetabchange
28407          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28408          * @param {Roo.TabPanel} this
28409          * @param {Object} e Set cancel to true on this object to cancel the tab change
28410          * @param {Roo.TabPanelItem} tab The tab being changed to
28411          */
28412         "beforetabchange" : true
28413     });
28414
28415     Roo.EventManager.onWindowResize(this.onResize, this);
28416     this.cpad = this.el.getPadding("lr");
28417     this.hiddenCount = 0;
28418
28419
28420     // toolbar on the tabbar support...
28421     if (this.toolbar) {
28422         var tcfg = this.toolbar;
28423         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28424         this.toolbar = new Roo.Toolbar(tcfg);
28425         if (Roo.isSafari) {
28426             var tbl = tcfg.container.child('table', true);
28427             tbl.setAttribute('width', '100%');
28428         }
28429         
28430     }
28431    
28432
28433
28434     Roo.TabPanel.superclass.constructor.call(this);
28435 };
28436
28437 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28438     /*
28439      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28440      */
28441     tabPosition : "top",
28442     /*
28443      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28444      */
28445     currentTabWidth : 0,
28446     /*
28447      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28448      */
28449     minTabWidth : 40,
28450     /*
28451      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28452      */
28453     maxTabWidth : 250,
28454     /*
28455      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28456      */
28457     preferredTabWidth : 175,
28458     /*
28459      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28460      */
28461     resizeTabs : false,
28462     /*
28463      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28464      */
28465     monitorResize : true,
28466     /*
28467      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28468      */
28469     toolbar : false,
28470
28471     /**
28472      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28473      * @param {String} id The id of the div to use <b>or create</b>
28474      * @param {String} text The text for the tab
28475      * @param {String} content (optional) Content to put in the TabPanelItem body
28476      * @param {Boolean} closable (optional) True to create a close icon on the tab
28477      * @return {Roo.TabPanelItem} The created TabPanelItem
28478      */
28479     addTab : function(id, text, content, closable){
28480         var item = new Roo.TabPanelItem(this, id, text, closable);
28481         this.addTabItem(item);
28482         if(content){
28483             item.setContent(content);
28484         }
28485         return item;
28486     },
28487
28488     /**
28489      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28490      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28491      * @return {Roo.TabPanelItem}
28492      */
28493     getTab : function(id){
28494         return this.items[id];
28495     },
28496
28497     /**
28498      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28499      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28500      */
28501     hideTab : function(id){
28502         var t = this.items[id];
28503         if(!t.isHidden()){
28504            t.setHidden(true);
28505            this.hiddenCount++;
28506            this.autoSizeTabs();
28507         }
28508     },
28509
28510     /**
28511      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28512      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28513      */
28514     unhideTab : function(id){
28515         var t = this.items[id];
28516         if(t.isHidden()){
28517            t.setHidden(false);
28518            this.hiddenCount--;
28519            this.autoSizeTabs();
28520         }
28521     },
28522
28523     /**
28524      * Adds an existing {@link Roo.TabPanelItem}.
28525      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28526      */
28527     addTabItem : function(item){
28528         this.items[item.id] = item;
28529         this.items.push(item);
28530         if(this.resizeTabs){
28531            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28532            this.autoSizeTabs();
28533         }else{
28534             item.autoSize();
28535         }
28536     },
28537
28538     /**
28539      * Removes a {@link Roo.TabPanelItem}.
28540      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28541      */
28542     removeTab : function(id){
28543         var items = this.items;
28544         var tab = items[id];
28545         if(!tab) { return; }
28546         var index = items.indexOf(tab);
28547         if(this.active == tab && items.length > 1){
28548             var newTab = this.getNextAvailable(index);
28549             if(newTab) {
28550                 newTab.activate();
28551             }
28552         }
28553         this.stripEl.dom.removeChild(tab.pnode.dom);
28554         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28555             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28556         }
28557         items.splice(index, 1);
28558         delete this.items[tab.id];
28559         tab.fireEvent("close", tab);
28560         tab.purgeListeners();
28561         this.autoSizeTabs();
28562     },
28563
28564     getNextAvailable : function(start){
28565         var items = this.items;
28566         var index = start;
28567         // look for a next tab that will slide over to
28568         // replace the one being removed
28569         while(index < items.length){
28570             var item = items[++index];
28571             if(item && !item.isHidden()){
28572                 return item;
28573             }
28574         }
28575         // if one isn't found select the previous tab (on the left)
28576         index = start;
28577         while(index >= 0){
28578             var item = items[--index];
28579             if(item && !item.isHidden()){
28580                 return item;
28581             }
28582         }
28583         return null;
28584     },
28585
28586     /**
28587      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28588      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28589      */
28590     disableTab : function(id){
28591         var tab = this.items[id];
28592         if(tab && this.active != tab){
28593             tab.disable();
28594         }
28595     },
28596
28597     /**
28598      * Enables a {@link Roo.TabPanelItem} that is disabled.
28599      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28600      */
28601     enableTab : function(id){
28602         var tab = this.items[id];
28603         tab.enable();
28604     },
28605
28606     /**
28607      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28608      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28609      * @return {Roo.TabPanelItem} The TabPanelItem.
28610      */
28611     activate : function(id){
28612         var tab = this.items[id];
28613         if(!tab){
28614             return null;
28615         }
28616         if(tab == this.active || tab.disabled){
28617             return tab;
28618         }
28619         var e = {};
28620         this.fireEvent("beforetabchange", this, e, tab);
28621         if(e.cancel !== true && !tab.disabled){
28622             if(this.active){
28623                 this.active.hide();
28624             }
28625             this.active = this.items[id];
28626             this.active.show();
28627             this.fireEvent("tabchange", this, this.active);
28628         }
28629         return tab;
28630     },
28631
28632     /**
28633      * Gets the active {@link Roo.TabPanelItem}.
28634      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28635      */
28636     getActiveTab : function(){
28637         return this.active;
28638     },
28639
28640     /**
28641      * Updates the tab body element to fit the height of the container element
28642      * for overflow scrolling
28643      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28644      */
28645     syncHeight : function(targetHeight){
28646         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28647         var bm = this.bodyEl.getMargins();
28648         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28649         this.bodyEl.setHeight(newHeight);
28650         return newHeight;
28651     },
28652
28653     onResize : function(){
28654         if(this.monitorResize){
28655             this.autoSizeTabs();
28656         }
28657     },
28658
28659     /**
28660      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28661      */
28662     beginUpdate : function(){
28663         this.updating = true;
28664     },
28665
28666     /**
28667      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28668      */
28669     endUpdate : function(){
28670         this.updating = false;
28671         this.autoSizeTabs();
28672     },
28673
28674     /**
28675      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28676      */
28677     autoSizeTabs : function(){
28678         var count = this.items.length;
28679         var vcount = count - this.hiddenCount;
28680         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28681             return;
28682         }
28683         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28684         var availWidth = Math.floor(w / vcount);
28685         var b = this.stripBody;
28686         if(b.getWidth() > w){
28687             var tabs = this.items;
28688             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28689             if(availWidth < this.minTabWidth){
28690                 /*if(!this.sleft){    // incomplete scrolling code
28691                     this.createScrollButtons();
28692                 }
28693                 this.showScroll();
28694                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28695             }
28696         }else{
28697             if(this.currentTabWidth < this.preferredTabWidth){
28698                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28699             }
28700         }
28701     },
28702
28703     /**
28704      * Returns the number of tabs in this TabPanel.
28705      * @return {Number}
28706      */
28707      getCount : function(){
28708          return this.items.length;
28709      },
28710
28711     /**
28712      * Resizes all the tabs to the passed width
28713      * @param {Number} The new width
28714      */
28715     setTabWidth : function(width){
28716         this.currentTabWidth = width;
28717         for(var i = 0, len = this.items.length; i < len; i++) {
28718                 if(!this.items[i].isHidden()) {
28719                 this.items[i].setWidth(width);
28720             }
28721         }
28722     },
28723
28724     /**
28725      * Destroys this TabPanel
28726      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28727      */
28728     destroy : function(removeEl){
28729         Roo.EventManager.removeResizeListener(this.onResize, this);
28730         for(var i = 0, len = this.items.length; i < len; i++){
28731             this.items[i].purgeListeners();
28732         }
28733         if(removeEl === true){
28734             this.el.update("");
28735             this.el.remove();
28736         }
28737     }
28738 });
28739
28740 /**
28741  * @class Roo.TabPanelItem
28742  * @extends Roo.util.Observable
28743  * Represents an individual item (tab plus body) in a TabPanel.
28744  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28745  * @param {String} id The id of this TabPanelItem
28746  * @param {String} text The text for the tab of this TabPanelItem
28747  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28748  */
28749 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28750     /**
28751      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28752      * @type Roo.TabPanel
28753      */
28754     this.tabPanel = tabPanel;
28755     /**
28756      * The id for this TabPanelItem
28757      * @type String
28758      */
28759     this.id = id;
28760     /** @private */
28761     this.disabled = false;
28762     /** @private */
28763     this.text = text;
28764     /** @private */
28765     this.loaded = false;
28766     this.closable = closable;
28767
28768     /**
28769      * The body element for this TabPanelItem.
28770      * @type Roo.Element
28771      */
28772     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28773     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28774     this.bodyEl.setStyle("display", "block");
28775     this.bodyEl.setStyle("zoom", "1");
28776     this.hideAction();
28777
28778     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28779     /** @private */
28780     this.el = Roo.get(els.el, true);
28781     this.inner = Roo.get(els.inner, true);
28782     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28783     this.pnode = Roo.get(els.el.parentNode, true);
28784     this.el.on("mousedown", this.onTabMouseDown, this);
28785     this.el.on("click", this.onTabClick, this);
28786     /** @private */
28787     if(closable){
28788         var c = Roo.get(els.close, true);
28789         c.dom.title = this.closeText;
28790         c.addClassOnOver("close-over");
28791         c.on("click", this.closeClick, this);
28792      }
28793
28794     this.addEvents({
28795          /**
28796          * @event activate
28797          * Fires when this tab becomes the active tab.
28798          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28799          * @param {Roo.TabPanelItem} this
28800          */
28801         "activate": true,
28802         /**
28803          * @event beforeclose
28804          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28805          * @param {Roo.TabPanelItem} this
28806          * @param {Object} e Set cancel to true on this object to cancel the close.
28807          */
28808         "beforeclose": true,
28809         /**
28810          * @event close
28811          * Fires when this tab is closed.
28812          * @param {Roo.TabPanelItem} this
28813          */
28814          "close": true,
28815         /**
28816          * @event deactivate
28817          * Fires when this tab is no longer the active tab.
28818          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28819          * @param {Roo.TabPanelItem} this
28820          */
28821          "deactivate" : true
28822     });
28823     this.hidden = false;
28824
28825     Roo.TabPanelItem.superclass.constructor.call(this);
28826 };
28827
28828 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28829     purgeListeners : function(){
28830        Roo.util.Observable.prototype.purgeListeners.call(this);
28831        this.el.removeAllListeners();
28832     },
28833     /**
28834      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28835      */
28836     show : function(){
28837         this.pnode.addClass("on");
28838         this.showAction();
28839         if(Roo.isOpera){
28840             this.tabPanel.stripWrap.repaint();
28841         }
28842         this.fireEvent("activate", this.tabPanel, this);
28843     },
28844
28845     /**
28846      * Returns true if this tab is the active tab.
28847      * @return {Boolean}
28848      */
28849     isActive : function(){
28850         return this.tabPanel.getActiveTab() == this;
28851     },
28852
28853     /**
28854      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28855      */
28856     hide : function(){
28857         this.pnode.removeClass("on");
28858         this.hideAction();
28859         this.fireEvent("deactivate", this.tabPanel, this);
28860     },
28861
28862     hideAction : function(){
28863         this.bodyEl.hide();
28864         this.bodyEl.setStyle("position", "absolute");
28865         this.bodyEl.setLeft("-20000px");
28866         this.bodyEl.setTop("-20000px");
28867     },
28868
28869     showAction : function(){
28870         this.bodyEl.setStyle("position", "relative");
28871         this.bodyEl.setTop("");
28872         this.bodyEl.setLeft("");
28873         this.bodyEl.show();
28874     },
28875
28876     /**
28877      * Set the tooltip for the tab.
28878      * @param {String} tooltip The tab's tooltip
28879      */
28880     setTooltip : function(text){
28881         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28882             this.textEl.dom.qtip = text;
28883             this.textEl.dom.removeAttribute('title');
28884         }else{
28885             this.textEl.dom.title = text;
28886         }
28887     },
28888
28889     onTabClick : function(e){
28890         e.preventDefault();
28891         this.tabPanel.activate(this.id);
28892     },
28893
28894     onTabMouseDown : function(e){
28895         e.preventDefault();
28896         this.tabPanel.activate(this.id);
28897     },
28898
28899     getWidth : function(){
28900         return this.inner.getWidth();
28901     },
28902
28903     setWidth : function(width){
28904         var iwidth = width - this.pnode.getPadding("lr");
28905         this.inner.setWidth(iwidth);
28906         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28907         this.pnode.setWidth(width);
28908     },
28909
28910     /**
28911      * Show or hide the tab
28912      * @param {Boolean} hidden True to hide or false to show.
28913      */
28914     setHidden : function(hidden){
28915         this.hidden = hidden;
28916         this.pnode.setStyle("display", hidden ? "none" : "");
28917     },
28918
28919     /**
28920      * Returns true if this tab is "hidden"
28921      * @return {Boolean}
28922      */
28923     isHidden : function(){
28924         return this.hidden;
28925     },
28926
28927     /**
28928      * Returns the text for this tab
28929      * @return {String}
28930      */
28931     getText : function(){
28932         return this.text;
28933     },
28934
28935     autoSize : function(){
28936         //this.el.beginMeasure();
28937         this.textEl.setWidth(1);
28938         /*
28939          *  #2804 [new] Tabs in Roojs
28940          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28941          */
28942         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28943         //this.el.endMeasure();
28944     },
28945
28946     /**
28947      * Sets the text for the tab (Note: this also sets the tooltip text)
28948      * @param {String} text The tab's text and tooltip
28949      */
28950     setText : function(text){
28951         this.text = text;
28952         this.textEl.update(text);
28953         this.setTooltip(text);
28954         if(!this.tabPanel.resizeTabs){
28955             this.autoSize();
28956         }
28957     },
28958     /**
28959      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28960      */
28961     activate : function(){
28962         this.tabPanel.activate(this.id);
28963     },
28964
28965     /**
28966      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28967      */
28968     disable : function(){
28969         if(this.tabPanel.active != this){
28970             this.disabled = true;
28971             this.pnode.addClass("disabled");
28972         }
28973     },
28974
28975     /**
28976      * Enables this TabPanelItem if it was previously disabled.
28977      */
28978     enable : function(){
28979         this.disabled = false;
28980         this.pnode.removeClass("disabled");
28981     },
28982
28983     /**
28984      * Sets the content for this TabPanelItem.
28985      * @param {String} content The content
28986      * @param {Boolean} loadScripts true to look for and load scripts
28987      */
28988     setContent : function(content, loadScripts){
28989         this.bodyEl.update(content, loadScripts);
28990     },
28991
28992     /**
28993      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28994      * @return {Roo.UpdateManager} The UpdateManager
28995      */
28996     getUpdateManager : function(){
28997         return this.bodyEl.getUpdateManager();
28998     },
28999
29000     /**
29001      * Set a URL to be used to load the content for this TabPanelItem.
29002      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29003      * @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)
29004      * @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)
29005      * @return {Roo.UpdateManager} The UpdateManager
29006      */
29007     setUrl : function(url, params, loadOnce){
29008         if(this.refreshDelegate){
29009             this.un('activate', this.refreshDelegate);
29010         }
29011         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29012         this.on("activate", this.refreshDelegate);
29013         return this.bodyEl.getUpdateManager();
29014     },
29015
29016     /** @private */
29017     _handleRefresh : function(url, params, loadOnce){
29018         if(!loadOnce || !this.loaded){
29019             var updater = this.bodyEl.getUpdateManager();
29020             updater.update(url, params, this._setLoaded.createDelegate(this));
29021         }
29022     },
29023
29024     /**
29025      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29026      *   Will fail silently if the setUrl method has not been called.
29027      *   This does not activate the panel, just updates its content.
29028      */
29029     refresh : function(){
29030         if(this.refreshDelegate){
29031            this.loaded = false;
29032            this.refreshDelegate();
29033         }
29034     },
29035
29036     /** @private */
29037     _setLoaded : function(){
29038         this.loaded = true;
29039     },
29040
29041     /** @private */
29042     closeClick : function(e){
29043         var o = {};
29044         e.stopEvent();
29045         this.fireEvent("beforeclose", this, o);
29046         if(o.cancel !== true){
29047             this.tabPanel.removeTab(this.id);
29048         }
29049     },
29050     /**
29051      * The text displayed in the tooltip for the close icon.
29052      * @type String
29053      */
29054     closeText : "Close this tab"
29055 });
29056
29057 /** @private */
29058 Roo.TabPanel.prototype.createStrip = function(container){
29059     var strip = document.createElement("div");
29060     strip.className = "x-tabs-wrap";
29061     container.appendChild(strip);
29062     return strip;
29063 };
29064 /** @private */
29065 Roo.TabPanel.prototype.createStripList = function(strip){
29066     // div wrapper for retard IE
29067     // returns the "tr" element.
29068     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29069         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29070         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29071     return strip.firstChild.firstChild.firstChild.firstChild;
29072 };
29073 /** @private */
29074 Roo.TabPanel.prototype.createBody = function(container){
29075     var body = document.createElement("div");
29076     Roo.id(body, "tab-body");
29077     Roo.fly(body).addClass("x-tabs-body");
29078     container.appendChild(body);
29079     return body;
29080 };
29081 /** @private */
29082 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29083     var body = Roo.getDom(id);
29084     if(!body){
29085         body = document.createElement("div");
29086         body.id = id;
29087     }
29088     Roo.fly(body).addClass("x-tabs-item-body");
29089     bodyEl.insertBefore(body, bodyEl.firstChild);
29090     return body;
29091 };
29092 /** @private */
29093 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29094     var td = document.createElement("td");
29095     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29096     //stripEl.appendChild(td);
29097     if(closable){
29098         td.className = "x-tabs-closable";
29099         if(!this.closeTpl){
29100             this.closeTpl = new Roo.Template(
29101                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29102                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29103                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29104             );
29105         }
29106         var el = this.closeTpl.overwrite(td, {"text": text});
29107         var close = el.getElementsByTagName("div")[0];
29108         var inner = el.getElementsByTagName("em")[0];
29109         return {"el": el, "close": close, "inner": inner};
29110     } else {
29111         if(!this.tabTpl){
29112             this.tabTpl = new Roo.Template(
29113                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29114                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29115             );
29116         }
29117         var el = this.tabTpl.overwrite(td, {"text": text});
29118         var inner = el.getElementsByTagName("em")[0];
29119         return {"el": el, "inner": inner};
29120     }
29121 };/*
29122  * Based on:
29123  * Ext JS Library 1.1.1
29124  * Copyright(c) 2006-2007, Ext JS, LLC.
29125  *
29126  * Originally Released Under LGPL - original licence link has changed is not relivant.
29127  *
29128  * Fork - LGPL
29129  * <script type="text/javascript">
29130  */
29131
29132 /**
29133  * @class Roo.Button
29134  * @extends Roo.util.Observable
29135  * Simple Button class
29136  * @cfg {String} text The button text
29137  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29138  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29139  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29140  * @cfg {Object} scope The scope of the handler
29141  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29142  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29143  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29144  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29145  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29146  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29147    applies if enableToggle = true)
29148  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29149  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29150   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29151  * @constructor
29152  * Create a new button
29153  * @param {Object} config The config object
29154  */
29155 Roo.Button = function(renderTo, config)
29156 {
29157     if (!config) {
29158         config = renderTo;
29159         renderTo = config.renderTo || false;
29160     }
29161     
29162     Roo.apply(this, config);
29163     this.addEvents({
29164         /**
29165              * @event click
29166              * Fires when this button is clicked
29167              * @param {Button} this
29168              * @param {EventObject} e The click event
29169              */
29170             "click" : true,
29171         /**
29172              * @event toggle
29173              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29174              * @param {Button} this
29175              * @param {Boolean} pressed
29176              */
29177             "toggle" : true,
29178         /**
29179              * @event mouseover
29180              * Fires when the mouse hovers over the button
29181              * @param {Button} this
29182              * @param {Event} e The event object
29183              */
29184         'mouseover' : true,
29185         /**
29186              * @event mouseout
29187              * Fires when the mouse exits the button
29188              * @param {Button} this
29189              * @param {Event} e The event object
29190              */
29191         'mouseout': true,
29192          /**
29193              * @event render
29194              * Fires when the button is rendered
29195              * @param {Button} this
29196              */
29197         'render': true
29198     });
29199     if(this.menu){
29200         this.menu = Roo.menu.MenuMgr.get(this.menu);
29201     }
29202     // register listeners first!!  - so render can be captured..
29203     Roo.util.Observable.call(this);
29204     if(renderTo){
29205         this.render(renderTo);
29206     }
29207     
29208   
29209 };
29210
29211 Roo.extend(Roo.Button, Roo.util.Observable, {
29212     /**
29213      * 
29214      */
29215     
29216     /**
29217      * Read-only. True if this button is hidden
29218      * @type Boolean
29219      */
29220     hidden : false,
29221     /**
29222      * Read-only. True if this button is disabled
29223      * @type Boolean
29224      */
29225     disabled : false,
29226     /**
29227      * Read-only. True if this button is pressed (only if enableToggle = true)
29228      * @type Boolean
29229      */
29230     pressed : false,
29231
29232     /**
29233      * @cfg {Number} tabIndex 
29234      * The DOM tabIndex for this button (defaults to undefined)
29235      */
29236     tabIndex : undefined,
29237
29238     /**
29239      * @cfg {Boolean} enableToggle
29240      * True to enable pressed/not pressed toggling (defaults to false)
29241      */
29242     enableToggle: false,
29243     /**
29244      * @cfg {Mixed} menu
29245      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29246      */
29247     menu : undefined,
29248     /**
29249      * @cfg {String} menuAlign
29250      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29251      */
29252     menuAlign : "tl-bl?",
29253
29254     /**
29255      * @cfg {String} iconCls
29256      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29257      */
29258     iconCls : undefined,
29259     /**
29260      * @cfg {String} type
29261      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29262      */
29263     type : 'button',
29264
29265     // private
29266     menuClassTarget: 'tr',
29267
29268     /**
29269      * @cfg {String} clickEvent
29270      * The type of event to map to the button's event handler (defaults to 'click')
29271      */
29272     clickEvent : 'click',
29273
29274     /**
29275      * @cfg {Boolean} handleMouseEvents
29276      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29277      */
29278     handleMouseEvents : true,
29279
29280     /**
29281      * @cfg {String} tooltipType
29282      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29283      */
29284     tooltipType : 'qtip',
29285
29286     /**
29287      * @cfg {String} cls
29288      * A CSS class to apply to the button's main element.
29289      */
29290     
29291     /**
29292      * @cfg {Roo.Template} template (Optional)
29293      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29294      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29295      * require code modifications if required elements (e.g. a button) aren't present.
29296      */
29297
29298     // private
29299     render : function(renderTo){
29300         var btn;
29301         if(this.hideParent){
29302             this.parentEl = Roo.get(renderTo);
29303         }
29304         if(!this.dhconfig){
29305             if(!this.template){
29306                 if(!Roo.Button.buttonTemplate){
29307                     // hideous table template
29308                     Roo.Button.buttonTemplate = new Roo.Template(
29309                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29310                         '<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>',
29311                         "</tr></tbody></table>");
29312                 }
29313                 this.template = Roo.Button.buttonTemplate;
29314             }
29315             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29316             var btnEl = btn.child("button:first");
29317             btnEl.on('focus', this.onFocus, this);
29318             btnEl.on('blur', this.onBlur, this);
29319             if(this.cls){
29320                 btn.addClass(this.cls);
29321             }
29322             if(this.icon){
29323                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29324             }
29325             if(this.iconCls){
29326                 btnEl.addClass(this.iconCls);
29327                 if(!this.cls){
29328                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29329                 }
29330             }
29331             if(this.tabIndex !== undefined){
29332                 btnEl.dom.tabIndex = this.tabIndex;
29333             }
29334             if(this.tooltip){
29335                 if(typeof this.tooltip == 'object'){
29336                     Roo.QuickTips.tips(Roo.apply({
29337                           target: btnEl.id
29338                     }, this.tooltip));
29339                 } else {
29340                     btnEl.dom[this.tooltipType] = this.tooltip;
29341                 }
29342             }
29343         }else{
29344             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29345         }
29346         this.el = btn;
29347         if(this.id){
29348             this.el.dom.id = this.el.id = this.id;
29349         }
29350         if(this.menu){
29351             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29352             this.menu.on("show", this.onMenuShow, this);
29353             this.menu.on("hide", this.onMenuHide, this);
29354         }
29355         btn.addClass("x-btn");
29356         if(Roo.isIE && !Roo.isIE7){
29357             this.autoWidth.defer(1, this);
29358         }else{
29359             this.autoWidth();
29360         }
29361         if(this.handleMouseEvents){
29362             btn.on("mouseover", this.onMouseOver, this);
29363             btn.on("mouseout", this.onMouseOut, this);
29364             btn.on("mousedown", this.onMouseDown, this);
29365         }
29366         btn.on(this.clickEvent, this.onClick, this);
29367         //btn.on("mouseup", this.onMouseUp, this);
29368         if(this.hidden){
29369             this.hide();
29370         }
29371         if(this.disabled){
29372             this.disable();
29373         }
29374         Roo.ButtonToggleMgr.register(this);
29375         if(this.pressed){
29376             this.el.addClass("x-btn-pressed");
29377         }
29378         if(this.repeat){
29379             var repeater = new Roo.util.ClickRepeater(btn,
29380                 typeof this.repeat == "object" ? this.repeat : {}
29381             );
29382             repeater.on("click", this.onClick,  this);
29383         }
29384         
29385         this.fireEvent('render', this);
29386         
29387     },
29388     /**
29389      * Returns the button's underlying element
29390      * @return {Roo.Element} The element
29391      */
29392     getEl : function(){
29393         return this.el;  
29394     },
29395     
29396     /**
29397      * Destroys this Button and removes any listeners.
29398      */
29399     destroy : function(){
29400         Roo.ButtonToggleMgr.unregister(this);
29401         this.el.removeAllListeners();
29402         this.purgeListeners();
29403         this.el.remove();
29404     },
29405
29406     // private
29407     autoWidth : function(){
29408         if(this.el){
29409             this.el.setWidth("auto");
29410             if(Roo.isIE7 && Roo.isStrict){
29411                 var ib = this.el.child('button');
29412                 if(ib && ib.getWidth() > 20){
29413                     ib.clip();
29414                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29415                 }
29416             }
29417             if(this.minWidth){
29418                 if(this.hidden){
29419                     this.el.beginMeasure();
29420                 }
29421                 if(this.el.getWidth() < this.minWidth){
29422                     this.el.setWidth(this.minWidth);
29423                 }
29424                 if(this.hidden){
29425                     this.el.endMeasure();
29426                 }
29427             }
29428         }
29429     },
29430
29431     /**
29432      * Assigns this button's click handler
29433      * @param {Function} handler The function to call when the button is clicked
29434      * @param {Object} scope (optional) Scope for the function passed in
29435      */
29436     setHandler : function(handler, scope){
29437         this.handler = handler;
29438         this.scope = scope;  
29439     },
29440     
29441     /**
29442      * Sets this button's text
29443      * @param {String} text The button text
29444      */
29445     setText : function(text){
29446         this.text = text;
29447         if(this.el){
29448             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29449         }
29450         this.autoWidth();
29451     },
29452     
29453     /**
29454      * Gets the text for this button
29455      * @return {String} The button text
29456      */
29457     getText : function(){
29458         return this.text;  
29459     },
29460     
29461     /**
29462      * Show this button
29463      */
29464     show: function(){
29465         this.hidden = false;
29466         if(this.el){
29467             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29468         }
29469     },
29470     
29471     /**
29472      * Hide this button
29473      */
29474     hide: function(){
29475         this.hidden = true;
29476         if(this.el){
29477             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29478         }
29479     },
29480     
29481     /**
29482      * Convenience function for boolean show/hide
29483      * @param {Boolean} visible True to show, false to hide
29484      */
29485     setVisible: function(visible){
29486         if(visible) {
29487             this.show();
29488         }else{
29489             this.hide();
29490         }
29491     },
29492     
29493     /**
29494      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29495      * @param {Boolean} state (optional) Force a particular state
29496      */
29497     toggle : function(state){
29498         state = state === undefined ? !this.pressed : state;
29499         if(state != this.pressed){
29500             if(state){
29501                 this.el.addClass("x-btn-pressed");
29502                 this.pressed = true;
29503                 this.fireEvent("toggle", this, true);
29504             }else{
29505                 this.el.removeClass("x-btn-pressed");
29506                 this.pressed = false;
29507                 this.fireEvent("toggle", this, false);
29508             }
29509             if(this.toggleHandler){
29510                 this.toggleHandler.call(this.scope || this, this, state);
29511             }
29512         }
29513     },
29514     
29515     /**
29516      * Focus the button
29517      */
29518     focus : function(){
29519         this.el.child('button:first').focus();
29520     },
29521     
29522     /**
29523      * Disable this button
29524      */
29525     disable : function(){
29526         if(this.el){
29527             this.el.addClass("x-btn-disabled");
29528         }
29529         this.disabled = true;
29530     },
29531     
29532     /**
29533      * Enable this button
29534      */
29535     enable : function(){
29536         if(this.el){
29537             this.el.removeClass("x-btn-disabled");
29538         }
29539         this.disabled = false;
29540     },
29541
29542     /**
29543      * Convenience function for boolean enable/disable
29544      * @param {Boolean} enabled True to enable, false to disable
29545      */
29546     setDisabled : function(v){
29547         this[v !== true ? "enable" : "disable"]();
29548     },
29549
29550     // private
29551     onClick : function(e)
29552     {
29553         if(e){
29554             e.preventDefault();
29555         }
29556         if(e.button != 0){
29557             return;
29558         }
29559         if(!this.disabled){
29560             if(this.enableToggle){
29561                 this.toggle();
29562             }
29563             if(this.menu && !this.menu.isVisible()){
29564                 this.menu.show(this.el, this.menuAlign);
29565             }
29566             this.fireEvent("click", this, e);
29567             if(this.handler){
29568                 this.el.removeClass("x-btn-over");
29569                 this.handler.call(this.scope || this, this, e);
29570             }
29571         }
29572     },
29573     // private
29574     onMouseOver : function(e){
29575         if(!this.disabled){
29576             this.el.addClass("x-btn-over");
29577             this.fireEvent('mouseover', this, e);
29578         }
29579     },
29580     // private
29581     onMouseOut : function(e){
29582         if(!e.within(this.el,  true)){
29583             this.el.removeClass("x-btn-over");
29584             this.fireEvent('mouseout', this, e);
29585         }
29586     },
29587     // private
29588     onFocus : function(e){
29589         if(!this.disabled){
29590             this.el.addClass("x-btn-focus");
29591         }
29592     },
29593     // private
29594     onBlur : function(e){
29595         this.el.removeClass("x-btn-focus");
29596     },
29597     // private
29598     onMouseDown : function(e){
29599         if(!this.disabled && e.button == 0){
29600             this.el.addClass("x-btn-click");
29601             Roo.get(document).on('mouseup', this.onMouseUp, this);
29602         }
29603     },
29604     // private
29605     onMouseUp : function(e){
29606         if(e.button == 0){
29607             this.el.removeClass("x-btn-click");
29608             Roo.get(document).un('mouseup', this.onMouseUp, this);
29609         }
29610     },
29611     // private
29612     onMenuShow : function(e){
29613         this.el.addClass("x-btn-menu-active");
29614     },
29615     // private
29616     onMenuHide : function(e){
29617         this.el.removeClass("x-btn-menu-active");
29618     }   
29619 });
29620
29621 // Private utility class used by Button
29622 Roo.ButtonToggleMgr = function(){
29623    var groups = {};
29624    
29625    function toggleGroup(btn, state){
29626        if(state){
29627            var g = groups[btn.toggleGroup];
29628            for(var i = 0, l = g.length; i < l; i++){
29629                if(g[i] != btn){
29630                    g[i].toggle(false);
29631                }
29632            }
29633        }
29634    }
29635    
29636    return {
29637        register : function(btn){
29638            if(!btn.toggleGroup){
29639                return;
29640            }
29641            var g = groups[btn.toggleGroup];
29642            if(!g){
29643                g = groups[btn.toggleGroup] = [];
29644            }
29645            g.push(btn);
29646            btn.on("toggle", toggleGroup);
29647        },
29648        
29649        unregister : function(btn){
29650            if(!btn.toggleGroup){
29651                return;
29652            }
29653            var g = groups[btn.toggleGroup];
29654            if(g){
29655                g.remove(btn);
29656                btn.un("toggle", toggleGroup);
29657            }
29658        }
29659    };
29660 }();/*
29661  * Based on:
29662  * Ext JS Library 1.1.1
29663  * Copyright(c) 2006-2007, Ext JS, LLC.
29664  *
29665  * Originally Released Under LGPL - original licence link has changed is not relivant.
29666  *
29667  * Fork - LGPL
29668  * <script type="text/javascript">
29669  */
29670  
29671 /**
29672  * @class Roo.SplitButton
29673  * @extends Roo.Button
29674  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29675  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29676  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29677  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29678  * @cfg {String} arrowTooltip The title attribute of the arrow
29679  * @constructor
29680  * Create a new menu button
29681  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29682  * @param {Object} config The config object
29683  */
29684 Roo.SplitButton = function(renderTo, config){
29685     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29686     /**
29687      * @event arrowclick
29688      * Fires when this button's arrow is clicked
29689      * @param {SplitButton} this
29690      * @param {EventObject} e The click event
29691      */
29692     this.addEvents({"arrowclick":true});
29693 };
29694
29695 Roo.extend(Roo.SplitButton, Roo.Button, {
29696     render : function(renderTo){
29697         // this is one sweet looking template!
29698         var tpl = new Roo.Template(
29699             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29700             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29701             '<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>',
29702             "</tbody></table></td><td>",
29703             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29704             '<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>',
29705             "</tbody></table></td></tr></table>"
29706         );
29707         var btn = tpl.append(renderTo, [this.text, this.type], true);
29708         var btnEl = btn.child("button");
29709         if(this.cls){
29710             btn.addClass(this.cls);
29711         }
29712         if(this.icon){
29713             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29714         }
29715         if(this.iconCls){
29716             btnEl.addClass(this.iconCls);
29717             if(!this.cls){
29718                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29719             }
29720         }
29721         this.el = btn;
29722         if(this.handleMouseEvents){
29723             btn.on("mouseover", this.onMouseOver, this);
29724             btn.on("mouseout", this.onMouseOut, this);
29725             btn.on("mousedown", this.onMouseDown, this);
29726             btn.on("mouseup", this.onMouseUp, this);
29727         }
29728         btn.on(this.clickEvent, this.onClick, this);
29729         if(this.tooltip){
29730             if(typeof this.tooltip == 'object'){
29731                 Roo.QuickTips.tips(Roo.apply({
29732                       target: btnEl.id
29733                 }, this.tooltip));
29734             } else {
29735                 btnEl.dom[this.tooltipType] = this.tooltip;
29736             }
29737         }
29738         if(this.arrowTooltip){
29739             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29740         }
29741         if(this.hidden){
29742             this.hide();
29743         }
29744         if(this.disabled){
29745             this.disable();
29746         }
29747         if(this.pressed){
29748             this.el.addClass("x-btn-pressed");
29749         }
29750         if(Roo.isIE && !Roo.isIE7){
29751             this.autoWidth.defer(1, this);
29752         }else{
29753             this.autoWidth();
29754         }
29755         if(this.menu){
29756             this.menu.on("show", this.onMenuShow, this);
29757             this.menu.on("hide", this.onMenuHide, this);
29758         }
29759         this.fireEvent('render', this);
29760     },
29761
29762     // private
29763     autoWidth : function(){
29764         if(this.el){
29765             var tbl = this.el.child("table:first");
29766             var tbl2 = this.el.child("table:last");
29767             this.el.setWidth("auto");
29768             tbl.setWidth("auto");
29769             if(Roo.isIE7 && Roo.isStrict){
29770                 var ib = this.el.child('button:first');
29771                 if(ib && ib.getWidth() > 20){
29772                     ib.clip();
29773                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29774                 }
29775             }
29776             if(this.minWidth){
29777                 if(this.hidden){
29778                     this.el.beginMeasure();
29779                 }
29780                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29781                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29782                 }
29783                 if(this.hidden){
29784                     this.el.endMeasure();
29785                 }
29786             }
29787             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29788         } 
29789     },
29790     /**
29791      * Sets this button's click handler
29792      * @param {Function} handler The function to call when the button is clicked
29793      * @param {Object} scope (optional) Scope for the function passed above
29794      */
29795     setHandler : function(handler, scope){
29796         this.handler = handler;
29797         this.scope = scope;  
29798     },
29799     
29800     /**
29801      * Sets this button's arrow click handler
29802      * @param {Function} handler The function to call when the arrow is clicked
29803      * @param {Object} scope (optional) Scope for the function passed above
29804      */
29805     setArrowHandler : function(handler, scope){
29806         this.arrowHandler = handler;
29807         this.scope = scope;  
29808     },
29809     
29810     /**
29811      * Focus the button
29812      */
29813     focus : function(){
29814         if(this.el){
29815             this.el.child("button:first").focus();
29816         }
29817     },
29818
29819     // private
29820     onClick : function(e){
29821         e.preventDefault();
29822         if(!this.disabled){
29823             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29824                 if(this.menu && !this.menu.isVisible()){
29825                     this.menu.show(this.el, this.menuAlign);
29826                 }
29827                 this.fireEvent("arrowclick", this, e);
29828                 if(this.arrowHandler){
29829                     this.arrowHandler.call(this.scope || this, this, e);
29830                 }
29831             }else{
29832                 this.fireEvent("click", this, e);
29833                 if(this.handler){
29834                     this.handler.call(this.scope || this, this, e);
29835                 }
29836             }
29837         }
29838     },
29839     // private
29840     onMouseDown : function(e){
29841         if(!this.disabled){
29842             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29843         }
29844     },
29845     // private
29846     onMouseUp : function(e){
29847         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29848     }   
29849 });
29850
29851
29852 // backwards compat
29853 Roo.MenuButton = Roo.SplitButton;/*
29854  * Based on:
29855  * Ext JS Library 1.1.1
29856  * Copyright(c) 2006-2007, Ext JS, LLC.
29857  *
29858  * Originally Released Under LGPL - original licence link has changed is not relivant.
29859  *
29860  * Fork - LGPL
29861  * <script type="text/javascript">
29862  */
29863
29864 /**
29865  * @class Roo.Toolbar
29866  * Basic Toolbar class.
29867  * @constructor
29868  * Creates a new Toolbar
29869  * @param {Object} container The config object
29870  */ 
29871 Roo.Toolbar = function(container, buttons, config)
29872 {
29873     /// old consturctor format still supported..
29874     if(container instanceof Array){ // omit the container for later rendering
29875         buttons = container;
29876         config = buttons;
29877         container = null;
29878     }
29879     if (typeof(container) == 'object' && container.xtype) {
29880         config = container;
29881         container = config.container;
29882         buttons = config.buttons || []; // not really - use items!!
29883     }
29884     var xitems = [];
29885     if (config && config.items) {
29886         xitems = config.items;
29887         delete config.items;
29888     }
29889     Roo.apply(this, config);
29890     this.buttons = buttons;
29891     
29892     if(container){
29893         this.render(container);
29894     }
29895     this.xitems = xitems;
29896     Roo.each(xitems, function(b) {
29897         this.add(b);
29898     }, this);
29899     
29900 };
29901
29902 Roo.Toolbar.prototype = {
29903     /**
29904      * @cfg {Array} items
29905      * array of button configs or elements to add (will be converted to a MixedCollection)
29906      */
29907     
29908     /**
29909      * @cfg {String/HTMLElement/Element} container
29910      * The id or element that will contain the toolbar
29911      */
29912     // private
29913     render : function(ct){
29914         this.el = Roo.get(ct);
29915         if(this.cls){
29916             this.el.addClass(this.cls);
29917         }
29918         // using a table allows for vertical alignment
29919         // 100% width is needed by Safari...
29920         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29921         this.tr = this.el.child("tr", true);
29922         var autoId = 0;
29923         this.items = new Roo.util.MixedCollection(false, function(o){
29924             return o.id || ("item" + (++autoId));
29925         });
29926         if(this.buttons){
29927             this.add.apply(this, this.buttons);
29928             delete this.buttons;
29929         }
29930     },
29931
29932     /**
29933      * Adds element(s) to the toolbar -- this function takes a variable number of 
29934      * arguments of mixed type and adds them to the toolbar.
29935      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29936      * <ul>
29937      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29938      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29939      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29940      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29941      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29942      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29943      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29944      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29945      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29946      * </ul>
29947      * @param {Mixed} arg2
29948      * @param {Mixed} etc.
29949      */
29950     add : function(){
29951         var a = arguments, l = a.length;
29952         for(var i = 0; i < l; i++){
29953             this._add(a[i]);
29954         }
29955     },
29956     // private..
29957     _add : function(el) {
29958         
29959         if (el.xtype) {
29960             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29961         }
29962         
29963         if (el.applyTo){ // some kind of form field
29964             return this.addField(el);
29965         } 
29966         if (el.render){ // some kind of Toolbar.Item
29967             return this.addItem(el);
29968         }
29969         if (typeof el == "string"){ // string
29970             if(el == "separator" || el == "-"){
29971                 return this.addSeparator();
29972             }
29973             if (el == " "){
29974                 return this.addSpacer();
29975             }
29976             if(el == "->"){
29977                 return this.addFill();
29978             }
29979             return this.addText(el);
29980             
29981         }
29982         if(el.tagName){ // element
29983             return this.addElement(el);
29984         }
29985         if(typeof el == "object"){ // must be button config?
29986             return this.addButton(el);
29987         }
29988         // and now what?!?!
29989         return false;
29990         
29991     },
29992     
29993     /**
29994      * Add an Xtype element
29995      * @param {Object} xtype Xtype Object
29996      * @return {Object} created Object
29997      */
29998     addxtype : function(e){
29999         return this.add(e);  
30000     },
30001     
30002     /**
30003      * Returns the Element for this toolbar.
30004      * @return {Roo.Element}
30005      */
30006     getEl : function(){
30007         return this.el;  
30008     },
30009     
30010     /**
30011      * Adds a separator
30012      * @return {Roo.Toolbar.Item} The separator item
30013      */
30014     addSeparator : function(){
30015         return this.addItem(new Roo.Toolbar.Separator());
30016     },
30017
30018     /**
30019      * Adds a spacer element
30020      * @return {Roo.Toolbar.Spacer} The spacer item
30021      */
30022     addSpacer : function(){
30023         return this.addItem(new Roo.Toolbar.Spacer());
30024     },
30025
30026     /**
30027      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30028      * @return {Roo.Toolbar.Fill} The fill item
30029      */
30030     addFill : function(){
30031         return this.addItem(new Roo.Toolbar.Fill());
30032     },
30033
30034     /**
30035      * Adds any standard HTML element to the toolbar
30036      * @param {String/HTMLElement/Element} el The element or id of the element to add
30037      * @return {Roo.Toolbar.Item} The element's item
30038      */
30039     addElement : function(el){
30040         return this.addItem(new Roo.Toolbar.Item(el));
30041     },
30042     /**
30043      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30044      * @type Roo.util.MixedCollection  
30045      */
30046     items : false,
30047      
30048     /**
30049      * Adds any Toolbar.Item or subclass
30050      * @param {Roo.Toolbar.Item} item
30051      * @return {Roo.Toolbar.Item} The item
30052      */
30053     addItem : function(item){
30054         var td = this.nextBlock();
30055         item.render(td);
30056         this.items.add(item);
30057         return item;
30058     },
30059     
30060     /**
30061      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30062      * @param {Object/Array} config A button config or array of configs
30063      * @return {Roo.Toolbar.Button/Array}
30064      */
30065     addButton : function(config){
30066         if(config instanceof Array){
30067             var buttons = [];
30068             for(var i = 0, len = config.length; i < len; i++) {
30069                 buttons.push(this.addButton(config[i]));
30070             }
30071             return buttons;
30072         }
30073         var b = config;
30074         if(!(config instanceof Roo.Toolbar.Button)){
30075             b = config.split ?
30076                 new Roo.Toolbar.SplitButton(config) :
30077                 new Roo.Toolbar.Button(config);
30078         }
30079         var td = this.nextBlock();
30080         b.render(td);
30081         this.items.add(b);
30082         return b;
30083     },
30084     
30085     /**
30086      * Adds text to the toolbar
30087      * @param {String} text The text to add
30088      * @return {Roo.Toolbar.Item} The element's item
30089      */
30090     addText : function(text){
30091         return this.addItem(new Roo.Toolbar.TextItem(text));
30092     },
30093     
30094     /**
30095      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30096      * @param {Number} index The index where the item is to be inserted
30097      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30098      * @return {Roo.Toolbar.Button/Item}
30099      */
30100     insertButton : function(index, item){
30101         if(item instanceof Array){
30102             var buttons = [];
30103             for(var i = 0, len = item.length; i < len; i++) {
30104                buttons.push(this.insertButton(index + i, item[i]));
30105             }
30106             return buttons;
30107         }
30108         if (!(item instanceof Roo.Toolbar.Button)){
30109            item = new Roo.Toolbar.Button(item);
30110         }
30111         var td = document.createElement("td");
30112         this.tr.insertBefore(td, this.tr.childNodes[index]);
30113         item.render(td);
30114         this.items.insert(index, item);
30115         return item;
30116     },
30117     
30118     /**
30119      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30120      * @param {Object} config
30121      * @return {Roo.Toolbar.Item} The element's item
30122      */
30123     addDom : function(config, returnEl){
30124         var td = this.nextBlock();
30125         Roo.DomHelper.overwrite(td, config);
30126         var ti = new Roo.Toolbar.Item(td.firstChild);
30127         ti.render(td);
30128         this.items.add(ti);
30129         return ti;
30130     },
30131
30132     /**
30133      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30134      * @type Roo.util.MixedCollection  
30135      */
30136     fields : false,
30137     
30138     /**
30139      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30140      * Note: the field should not have been rendered yet. For a field that has already been
30141      * rendered, use {@link #addElement}.
30142      * @param {Roo.form.Field} field
30143      * @return {Roo.ToolbarItem}
30144      */
30145      
30146       
30147     addField : function(field) {
30148         if (!this.fields) {
30149             var autoId = 0;
30150             this.fields = new Roo.util.MixedCollection(false, function(o){
30151                 return o.id || ("item" + (++autoId));
30152             });
30153
30154         }
30155         
30156         var td = this.nextBlock();
30157         field.render(td);
30158         var ti = new Roo.Toolbar.Item(td.firstChild);
30159         ti.render(td);
30160         this.items.add(ti);
30161         this.fields.add(field);
30162         return ti;
30163     },
30164     /**
30165      * Hide the toolbar
30166      * @method hide
30167      */
30168      
30169       
30170     hide : function()
30171     {
30172         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30173         this.el.child('div').hide();
30174     },
30175     /**
30176      * Show the toolbar
30177      * @method show
30178      */
30179     show : function()
30180     {
30181         this.el.child('div').show();
30182     },
30183       
30184     // private
30185     nextBlock : function(){
30186         var td = document.createElement("td");
30187         this.tr.appendChild(td);
30188         return td;
30189     },
30190
30191     // private
30192     destroy : function(){
30193         if(this.items){ // rendered?
30194             Roo.destroy.apply(Roo, this.items.items);
30195         }
30196         if(this.fields){ // rendered?
30197             Roo.destroy.apply(Roo, this.fields.items);
30198         }
30199         Roo.Element.uncache(this.el, this.tr);
30200     }
30201 };
30202
30203 /**
30204  * @class Roo.Toolbar.Item
30205  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30206  * @constructor
30207  * Creates a new Item
30208  * @param {HTMLElement} el 
30209  */
30210 Roo.Toolbar.Item = function(el){
30211     var cfg = {};
30212     if (typeof (el.xtype) != 'undefined') {
30213         cfg = el;
30214         el = cfg.el;
30215     }
30216     
30217     this.el = Roo.getDom(el);
30218     this.id = Roo.id(this.el);
30219     this.hidden = false;
30220     
30221     this.addEvents({
30222          /**
30223              * @event render
30224              * Fires when the button is rendered
30225              * @param {Button} this
30226              */
30227         'render': true
30228     });
30229     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30230 };
30231 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30232 //Roo.Toolbar.Item.prototype = {
30233     
30234     /**
30235      * Get this item's HTML Element
30236      * @return {HTMLElement}
30237      */
30238     getEl : function(){
30239        return this.el;  
30240     },
30241
30242     // private
30243     render : function(td){
30244         
30245          this.td = td;
30246         td.appendChild(this.el);
30247         
30248         this.fireEvent('render', this);
30249     },
30250     
30251     /**
30252      * Removes and destroys this item.
30253      */
30254     destroy : function(){
30255         this.td.parentNode.removeChild(this.td);
30256     },
30257     
30258     /**
30259      * Shows this item.
30260      */
30261     show: function(){
30262         this.hidden = false;
30263         this.td.style.display = "";
30264     },
30265     
30266     /**
30267      * Hides this item.
30268      */
30269     hide: function(){
30270         this.hidden = true;
30271         this.td.style.display = "none";
30272     },
30273     
30274     /**
30275      * Convenience function for boolean show/hide.
30276      * @param {Boolean} visible true to show/false to hide
30277      */
30278     setVisible: function(visible){
30279         if(visible) {
30280             this.show();
30281         }else{
30282             this.hide();
30283         }
30284     },
30285     
30286     /**
30287      * Try to focus this item.
30288      */
30289     focus : function(){
30290         Roo.fly(this.el).focus();
30291     },
30292     
30293     /**
30294      * Disables this item.
30295      */
30296     disable : function(){
30297         Roo.fly(this.td).addClass("x-item-disabled");
30298         this.disabled = true;
30299         this.el.disabled = true;
30300     },
30301     
30302     /**
30303      * Enables this item.
30304      */
30305     enable : function(){
30306         Roo.fly(this.td).removeClass("x-item-disabled");
30307         this.disabled = false;
30308         this.el.disabled = false;
30309     }
30310 });
30311
30312
30313 /**
30314  * @class Roo.Toolbar.Separator
30315  * @extends Roo.Toolbar.Item
30316  * A simple toolbar separator class
30317  * @constructor
30318  * Creates a new Separator
30319  */
30320 Roo.Toolbar.Separator = function(cfg){
30321     
30322     var s = document.createElement("span");
30323     s.className = "ytb-sep";
30324     if (cfg) {
30325         cfg.el = s;
30326     }
30327     
30328     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30329 };
30330 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30331     enable:Roo.emptyFn,
30332     disable:Roo.emptyFn,
30333     focus:Roo.emptyFn
30334 });
30335
30336 /**
30337  * @class Roo.Toolbar.Spacer
30338  * @extends Roo.Toolbar.Item
30339  * A simple element that adds extra horizontal space to a toolbar.
30340  * @constructor
30341  * Creates a new Spacer
30342  */
30343 Roo.Toolbar.Spacer = function(cfg){
30344     var s = document.createElement("div");
30345     s.className = "ytb-spacer";
30346     if (cfg) {
30347         cfg.el = s;
30348     }
30349     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30350 };
30351 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30352     enable:Roo.emptyFn,
30353     disable:Roo.emptyFn,
30354     focus:Roo.emptyFn
30355 });
30356
30357 /**
30358  * @class Roo.Toolbar.Fill
30359  * @extends Roo.Toolbar.Spacer
30360  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30361  * @constructor
30362  * Creates a new Spacer
30363  */
30364 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30365     // private
30366     render : function(td){
30367         td.style.width = '100%';
30368         Roo.Toolbar.Fill.superclass.render.call(this, td);
30369     }
30370 });
30371
30372 /**
30373  * @class Roo.Toolbar.TextItem
30374  * @extends Roo.Toolbar.Item
30375  * A simple class that renders text directly into a toolbar.
30376  * @constructor
30377  * Creates a new TextItem
30378  * @param {String} text
30379  */
30380 Roo.Toolbar.TextItem = function(cfg){
30381     var  text = cfg || "";
30382     if (typeof(cfg) == 'object') {
30383         text = cfg.text || "";
30384     }  else {
30385         cfg = null;
30386     }
30387     var s = document.createElement("span");
30388     s.className = "ytb-text";
30389     s.innerHTML = text;
30390     if (cfg) {
30391         cfg.el  = s;
30392     }
30393     
30394     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30395 };
30396 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30397     
30398      
30399     enable:Roo.emptyFn,
30400     disable:Roo.emptyFn,
30401     focus:Roo.emptyFn
30402 });
30403
30404 /**
30405  * @class Roo.Toolbar.Button
30406  * @extends Roo.Button
30407  * A button that renders into a toolbar.
30408  * @constructor
30409  * Creates a new Button
30410  * @param {Object} config A standard {@link Roo.Button} config object
30411  */
30412 Roo.Toolbar.Button = function(config){
30413     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30414 };
30415 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30416     render : function(td){
30417         this.td = td;
30418         Roo.Toolbar.Button.superclass.render.call(this, td);
30419     },
30420     
30421     /**
30422      * Removes and destroys this button
30423      */
30424     destroy : function(){
30425         Roo.Toolbar.Button.superclass.destroy.call(this);
30426         this.td.parentNode.removeChild(this.td);
30427     },
30428     
30429     /**
30430      * Shows this button
30431      */
30432     show: function(){
30433         this.hidden = false;
30434         this.td.style.display = "";
30435     },
30436     
30437     /**
30438      * Hides this button
30439      */
30440     hide: function(){
30441         this.hidden = true;
30442         this.td.style.display = "none";
30443     },
30444
30445     /**
30446      * Disables this item
30447      */
30448     disable : function(){
30449         Roo.fly(this.td).addClass("x-item-disabled");
30450         this.disabled = true;
30451     },
30452
30453     /**
30454      * Enables this item
30455      */
30456     enable : function(){
30457         Roo.fly(this.td).removeClass("x-item-disabled");
30458         this.disabled = false;
30459     }
30460 });
30461 // backwards compat
30462 Roo.ToolbarButton = Roo.Toolbar.Button;
30463
30464 /**
30465  * @class Roo.Toolbar.SplitButton
30466  * @extends Roo.SplitButton
30467  * A menu button that renders into a toolbar.
30468  * @constructor
30469  * Creates a new SplitButton
30470  * @param {Object} config A standard {@link Roo.SplitButton} config object
30471  */
30472 Roo.Toolbar.SplitButton = function(config){
30473     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30474 };
30475 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30476     render : function(td){
30477         this.td = td;
30478         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30479     },
30480     
30481     /**
30482      * Removes and destroys this button
30483      */
30484     destroy : function(){
30485         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30486         this.td.parentNode.removeChild(this.td);
30487     },
30488     
30489     /**
30490      * Shows this button
30491      */
30492     show: function(){
30493         this.hidden = false;
30494         this.td.style.display = "";
30495     },
30496     
30497     /**
30498      * Hides this button
30499      */
30500     hide: function(){
30501         this.hidden = true;
30502         this.td.style.display = "none";
30503     }
30504 });
30505
30506 // backwards compat
30507 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30508  * Based on:
30509  * Ext JS Library 1.1.1
30510  * Copyright(c) 2006-2007, Ext JS, LLC.
30511  *
30512  * Originally Released Under LGPL - original licence link has changed is not relivant.
30513  *
30514  * Fork - LGPL
30515  * <script type="text/javascript">
30516  */
30517  
30518 /**
30519  * @class Roo.PagingToolbar
30520  * @extends Roo.Toolbar
30521  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30522  * @constructor
30523  * Create a new PagingToolbar
30524  * @param {Object} config The config object
30525  */
30526 Roo.PagingToolbar = function(el, ds, config)
30527 {
30528     // old args format still supported... - xtype is prefered..
30529     if (typeof(el) == 'object' && el.xtype) {
30530         // created from xtype...
30531         config = el;
30532         ds = el.dataSource;
30533         el = config.container;
30534     }
30535     var items = [];
30536     if (config.items) {
30537         items = config.items;
30538         config.items = [];
30539     }
30540     
30541     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30542     this.ds = ds;
30543     this.cursor = 0;
30544     this.renderButtons(this.el);
30545     this.bind(ds);
30546     
30547     // supprot items array.
30548    
30549     Roo.each(items, function(e) {
30550         this.add(Roo.factory(e));
30551     },this);
30552     
30553 };
30554
30555 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30556     /**
30557      * @cfg {Roo.data.Store} dataSource
30558      * The underlying data store providing the paged data
30559      */
30560     /**
30561      * @cfg {String/HTMLElement/Element} container
30562      * container The id or element that will contain the toolbar
30563      */
30564     /**
30565      * @cfg {Boolean} displayInfo
30566      * True to display the displayMsg (defaults to false)
30567      */
30568     /**
30569      * @cfg {Number} pageSize
30570      * The number of records to display per page (defaults to 20)
30571      */
30572     pageSize: 20,
30573     /**
30574      * @cfg {String} displayMsg
30575      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30576      */
30577     displayMsg : 'Displaying {0} - {1} of {2}',
30578     /**
30579      * @cfg {String} emptyMsg
30580      * The message to display when no records are found (defaults to "No data to display")
30581      */
30582     emptyMsg : 'No data to display',
30583     /**
30584      * Customizable piece of the default paging text (defaults to "Page")
30585      * @type String
30586      */
30587     beforePageText : "Page",
30588     /**
30589      * Customizable piece of the default paging text (defaults to "of %0")
30590      * @type String
30591      */
30592     afterPageText : "of {0}",
30593     /**
30594      * Customizable piece of the default paging text (defaults to "First Page")
30595      * @type String
30596      */
30597     firstText : "First Page",
30598     /**
30599      * Customizable piece of the default paging text (defaults to "Previous Page")
30600      * @type String
30601      */
30602     prevText : "Previous Page",
30603     /**
30604      * Customizable piece of the default paging text (defaults to "Next Page")
30605      * @type String
30606      */
30607     nextText : "Next Page",
30608     /**
30609      * Customizable piece of the default paging text (defaults to "Last Page")
30610      * @type String
30611      */
30612     lastText : "Last Page",
30613     /**
30614      * Customizable piece of the default paging text (defaults to "Refresh")
30615      * @type String
30616      */
30617     refreshText : "Refresh",
30618
30619     // private
30620     renderButtons : function(el){
30621         Roo.PagingToolbar.superclass.render.call(this, el);
30622         this.first = this.addButton({
30623             tooltip: this.firstText,
30624             cls: "x-btn-icon x-grid-page-first",
30625             disabled: true,
30626             handler: this.onClick.createDelegate(this, ["first"])
30627         });
30628         this.prev = this.addButton({
30629             tooltip: this.prevText,
30630             cls: "x-btn-icon x-grid-page-prev",
30631             disabled: true,
30632             handler: this.onClick.createDelegate(this, ["prev"])
30633         });
30634         //this.addSeparator();
30635         this.add(this.beforePageText);
30636         this.field = Roo.get(this.addDom({
30637            tag: "input",
30638            type: "text",
30639            size: "3",
30640            value: "1",
30641            cls: "x-grid-page-number"
30642         }).el);
30643         this.field.on("keydown", this.onPagingKeydown, this);
30644         this.field.on("focus", function(){this.dom.select();});
30645         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30646         this.field.setHeight(18);
30647         //this.addSeparator();
30648         this.next = this.addButton({
30649             tooltip: this.nextText,
30650             cls: "x-btn-icon x-grid-page-next",
30651             disabled: true,
30652             handler: this.onClick.createDelegate(this, ["next"])
30653         });
30654         this.last = this.addButton({
30655             tooltip: this.lastText,
30656             cls: "x-btn-icon x-grid-page-last",
30657             disabled: true,
30658             handler: this.onClick.createDelegate(this, ["last"])
30659         });
30660         //this.addSeparator();
30661         this.loading = this.addButton({
30662             tooltip: this.refreshText,
30663             cls: "x-btn-icon x-grid-loading",
30664             handler: this.onClick.createDelegate(this, ["refresh"])
30665         });
30666
30667         if(this.displayInfo){
30668             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30669         }
30670     },
30671
30672     // private
30673     updateInfo : function(){
30674         if(this.displayEl){
30675             var count = this.ds.getCount();
30676             var msg = count == 0 ?
30677                 this.emptyMsg :
30678                 String.format(
30679                     this.displayMsg,
30680                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30681                 );
30682             this.displayEl.update(msg);
30683         }
30684     },
30685
30686     // private
30687     onLoad : function(ds, r, o){
30688        this.cursor = o.params ? o.params.start : 0;
30689        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30690
30691        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30692        this.field.dom.value = ap;
30693        this.first.setDisabled(ap == 1);
30694        this.prev.setDisabled(ap == 1);
30695        this.next.setDisabled(ap == ps);
30696        this.last.setDisabled(ap == ps);
30697        this.loading.enable();
30698        this.updateInfo();
30699     },
30700
30701     // private
30702     getPageData : function(){
30703         var total = this.ds.getTotalCount();
30704         return {
30705             total : total,
30706             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30707             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30708         };
30709     },
30710
30711     // private
30712     onLoadError : function(){
30713         this.loading.enable();
30714     },
30715
30716     // private
30717     onPagingKeydown : function(e){
30718         var k = e.getKey();
30719         var d = this.getPageData();
30720         if(k == e.RETURN){
30721             var v = this.field.dom.value, pageNum;
30722             if(!v || isNaN(pageNum = parseInt(v, 10))){
30723                 this.field.dom.value = d.activePage;
30724                 return;
30725             }
30726             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30727             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30728             e.stopEvent();
30729         }
30730         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))
30731         {
30732           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30733           this.field.dom.value = pageNum;
30734           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30735           e.stopEvent();
30736         }
30737         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30738         {
30739           var v = this.field.dom.value, pageNum; 
30740           var increment = (e.shiftKey) ? 10 : 1;
30741           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30742             increment *= -1;
30743           }
30744           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30745             this.field.dom.value = d.activePage;
30746             return;
30747           }
30748           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30749           {
30750             this.field.dom.value = parseInt(v, 10) + increment;
30751             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30752             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30753           }
30754           e.stopEvent();
30755         }
30756     },
30757
30758     // private
30759     beforeLoad : function(){
30760         if(this.loading){
30761             this.loading.disable();
30762         }
30763     },
30764
30765     // private
30766     onClick : function(which){
30767         var ds = this.ds;
30768         switch(which){
30769             case "first":
30770                 ds.load({params:{start: 0, limit: this.pageSize}});
30771             break;
30772             case "prev":
30773                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30774             break;
30775             case "next":
30776                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30777             break;
30778             case "last":
30779                 var total = ds.getTotalCount();
30780                 var extra = total % this.pageSize;
30781                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30782                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30783             break;
30784             case "refresh":
30785                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30786             break;
30787         }
30788     },
30789
30790     /**
30791      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30792      * @param {Roo.data.Store} store The data store to unbind
30793      */
30794     unbind : function(ds){
30795         ds.un("beforeload", this.beforeLoad, this);
30796         ds.un("load", this.onLoad, this);
30797         ds.un("loadexception", this.onLoadError, this);
30798         ds.un("remove", this.updateInfo, this);
30799         ds.un("add", this.updateInfo, this);
30800         this.ds = undefined;
30801     },
30802
30803     /**
30804      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30805      * @param {Roo.data.Store} store The data store to bind
30806      */
30807     bind : function(ds){
30808         ds.on("beforeload", this.beforeLoad, this);
30809         ds.on("load", this.onLoad, this);
30810         ds.on("loadexception", this.onLoadError, this);
30811         ds.on("remove", this.updateInfo, this);
30812         ds.on("add", this.updateInfo, this);
30813         this.ds = ds;
30814     }
30815 });/*
30816  * Based on:
30817  * Ext JS Library 1.1.1
30818  * Copyright(c) 2006-2007, Ext JS, LLC.
30819  *
30820  * Originally Released Under LGPL - original licence link has changed is not relivant.
30821  *
30822  * Fork - LGPL
30823  * <script type="text/javascript">
30824  */
30825
30826 /**
30827  * @class Roo.Resizable
30828  * @extends Roo.util.Observable
30829  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30830  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30831  * 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
30832  * the element will be wrapped for you automatically.</p>
30833  * <p>Here is the list of valid resize handles:</p>
30834  * <pre>
30835 Value   Description
30836 ------  -------------------
30837  'n'     north
30838  's'     south
30839  'e'     east
30840  'w'     west
30841  'nw'    northwest
30842  'sw'    southwest
30843  'se'    southeast
30844  'ne'    northeast
30845  'hd'    horizontal drag
30846  'all'   all
30847 </pre>
30848  * <p>Here's an example showing the creation of a typical Resizable:</p>
30849  * <pre><code>
30850 var resizer = new Roo.Resizable("element-id", {
30851     handles: 'all',
30852     minWidth: 200,
30853     minHeight: 100,
30854     maxWidth: 500,
30855     maxHeight: 400,
30856     pinned: true
30857 });
30858 resizer.on("resize", myHandler);
30859 </code></pre>
30860  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30861  * resizer.east.setDisplayed(false);</p>
30862  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30863  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30864  * resize operation's new size (defaults to [0, 0])
30865  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30866  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30867  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30868  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30869  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30870  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30871  * @cfg {Number} width The width of the element in pixels (defaults to null)
30872  * @cfg {Number} height The height of the element in pixels (defaults to null)
30873  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30874  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30875  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30876  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30877  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30878  * in favor of the handles config option (defaults to false)
30879  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30880  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30881  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30882  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30883  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30884  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30885  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30886  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30887  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30888  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30889  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30890  * @constructor
30891  * Create a new resizable component
30892  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30893  * @param {Object} config configuration options
30894   */
30895 Roo.Resizable = function(el, config)
30896 {
30897     this.el = Roo.get(el);
30898
30899     if(config && config.wrap){
30900         config.resizeChild = this.el;
30901         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30902         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30903         this.el.setStyle("overflow", "hidden");
30904         this.el.setPositioning(config.resizeChild.getPositioning());
30905         config.resizeChild.clearPositioning();
30906         if(!config.width || !config.height){
30907             var csize = config.resizeChild.getSize();
30908             this.el.setSize(csize.width, csize.height);
30909         }
30910         if(config.pinned && !config.adjustments){
30911             config.adjustments = "auto";
30912         }
30913     }
30914
30915     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30916     this.proxy.unselectable();
30917     this.proxy.enableDisplayMode('block');
30918
30919     Roo.apply(this, config);
30920
30921     if(this.pinned){
30922         this.disableTrackOver = true;
30923         this.el.addClass("x-resizable-pinned");
30924     }
30925     // if the element isn't positioned, make it relative
30926     var position = this.el.getStyle("position");
30927     if(position != "absolute" && position != "fixed"){
30928         this.el.setStyle("position", "relative");
30929     }
30930     if(!this.handles){ // no handles passed, must be legacy style
30931         this.handles = 's,e,se';
30932         if(this.multiDirectional){
30933             this.handles += ',n,w';
30934         }
30935     }
30936     if(this.handles == "all"){
30937         this.handles = "n s e w ne nw se sw";
30938     }
30939     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30940     var ps = Roo.Resizable.positions;
30941     for(var i = 0, len = hs.length; i < len; i++){
30942         if(hs[i] && ps[hs[i]]){
30943             var pos = ps[hs[i]];
30944             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30945         }
30946     }
30947     // legacy
30948     this.corner = this.southeast;
30949     
30950     // updateBox = the box can move..
30951     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30952         this.updateBox = true;
30953     }
30954
30955     this.activeHandle = null;
30956
30957     if(this.resizeChild){
30958         if(typeof this.resizeChild == "boolean"){
30959             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30960         }else{
30961             this.resizeChild = Roo.get(this.resizeChild, true);
30962         }
30963     }
30964     
30965     if(this.adjustments == "auto"){
30966         var rc = this.resizeChild;
30967         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30968         if(rc && (hw || hn)){
30969             rc.position("relative");
30970             rc.setLeft(hw ? hw.el.getWidth() : 0);
30971             rc.setTop(hn ? hn.el.getHeight() : 0);
30972         }
30973         this.adjustments = [
30974             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30975             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30976         ];
30977     }
30978
30979     if(this.draggable){
30980         this.dd = this.dynamic ?
30981             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30982         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30983     }
30984
30985     // public events
30986     this.addEvents({
30987         /**
30988          * @event beforeresize
30989          * Fired before resize is allowed. Set enabled to false to cancel resize.
30990          * @param {Roo.Resizable} this
30991          * @param {Roo.EventObject} e The mousedown event
30992          */
30993         "beforeresize" : true,
30994         /**
30995          * @event resizing
30996          * Fired a resizing.
30997          * @param {Roo.Resizable} this
30998          * @param {Number} x The new x position
30999          * @param {Number} y The new y position
31000          * @param {Number} w The new w width
31001          * @param {Number} h The new h hight
31002          * @param {Roo.EventObject} e The mouseup event
31003          */
31004         "resizing" : true,
31005         /**
31006          * @event resize
31007          * Fired after a resize.
31008          * @param {Roo.Resizable} this
31009          * @param {Number} width The new width
31010          * @param {Number} height The new height
31011          * @param {Roo.EventObject} e The mouseup event
31012          */
31013         "resize" : true
31014     });
31015
31016     if(this.width !== null && this.height !== null){
31017         this.resizeTo(this.width, this.height);
31018     }else{
31019         this.updateChildSize();
31020     }
31021     if(Roo.isIE){
31022         this.el.dom.style.zoom = 1;
31023     }
31024     Roo.Resizable.superclass.constructor.call(this);
31025 };
31026
31027 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31028         resizeChild : false,
31029         adjustments : [0, 0],
31030         minWidth : 5,
31031         minHeight : 5,
31032         maxWidth : 10000,
31033         maxHeight : 10000,
31034         enabled : true,
31035         animate : false,
31036         duration : .35,
31037         dynamic : false,
31038         handles : false,
31039         multiDirectional : false,
31040         disableTrackOver : false,
31041         easing : 'easeOutStrong',
31042         widthIncrement : 0,
31043         heightIncrement : 0,
31044         pinned : false,
31045         width : null,
31046         height : null,
31047         preserveRatio : false,
31048         transparent: false,
31049         minX: 0,
31050         minY: 0,
31051         draggable: false,
31052
31053         /**
31054          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31055          */
31056         constrainTo: undefined,
31057         /**
31058          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31059          */
31060         resizeRegion: undefined,
31061
31062
31063     /**
31064      * Perform a manual resize
31065      * @param {Number} width
31066      * @param {Number} height
31067      */
31068     resizeTo : function(width, height){
31069         this.el.setSize(width, height);
31070         this.updateChildSize();
31071         this.fireEvent("resize", this, width, height, null);
31072     },
31073
31074     // private
31075     startSizing : function(e, handle){
31076         this.fireEvent("beforeresize", this, e);
31077         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31078
31079             if(!this.overlay){
31080                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31081                 this.overlay.unselectable();
31082                 this.overlay.enableDisplayMode("block");
31083                 this.overlay.on("mousemove", this.onMouseMove, this);
31084                 this.overlay.on("mouseup", this.onMouseUp, this);
31085             }
31086             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31087
31088             this.resizing = true;
31089             this.startBox = this.el.getBox();
31090             this.startPoint = e.getXY();
31091             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31092                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31093
31094             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31095             this.overlay.show();
31096
31097             if(this.constrainTo) {
31098                 var ct = Roo.get(this.constrainTo);
31099                 this.resizeRegion = ct.getRegion().adjust(
31100                     ct.getFrameWidth('t'),
31101                     ct.getFrameWidth('l'),
31102                     -ct.getFrameWidth('b'),
31103                     -ct.getFrameWidth('r')
31104                 );
31105             }
31106
31107             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31108             this.proxy.show();
31109             this.proxy.setBox(this.startBox);
31110             if(!this.dynamic){
31111                 this.proxy.setStyle('visibility', 'visible');
31112             }
31113         }
31114     },
31115
31116     // private
31117     onMouseDown : function(handle, e){
31118         if(this.enabled){
31119             e.stopEvent();
31120             this.activeHandle = handle;
31121             this.startSizing(e, handle);
31122         }
31123     },
31124
31125     // private
31126     onMouseUp : function(e){
31127         var size = this.resizeElement();
31128         this.resizing = false;
31129         this.handleOut();
31130         this.overlay.hide();
31131         this.proxy.hide();
31132         this.fireEvent("resize", this, size.width, size.height, e);
31133     },
31134
31135     // private
31136     updateChildSize : function(){
31137         
31138         if(this.resizeChild){
31139             var el = this.el;
31140             var child = this.resizeChild;
31141             var adj = this.adjustments;
31142             if(el.dom.offsetWidth){
31143                 var b = el.getSize(true);
31144                 child.setSize(b.width+adj[0], b.height+adj[1]);
31145             }
31146             // Second call here for IE
31147             // The first call enables instant resizing and
31148             // the second call corrects scroll bars if they
31149             // exist
31150             if(Roo.isIE){
31151                 setTimeout(function(){
31152                     if(el.dom.offsetWidth){
31153                         var b = el.getSize(true);
31154                         child.setSize(b.width+adj[0], b.height+adj[1]);
31155                     }
31156                 }, 10);
31157             }
31158         }
31159     },
31160
31161     // private
31162     snap : function(value, inc, min){
31163         if(!inc || !value) {
31164             return value;
31165         }
31166         var newValue = value;
31167         var m = value % inc;
31168         if(m > 0){
31169             if(m > (inc/2)){
31170                 newValue = value + (inc-m);
31171             }else{
31172                 newValue = value - m;
31173             }
31174         }
31175         return Math.max(min, newValue);
31176     },
31177
31178     // private
31179     resizeElement : function(){
31180         var box = this.proxy.getBox();
31181         if(this.updateBox){
31182             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31183         }else{
31184             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31185         }
31186         this.updateChildSize();
31187         if(!this.dynamic){
31188             this.proxy.hide();
31189         }
31190         return box;
31191     },
31192
31193     // private
31194     constrain : function(v, diff, m, mx){
31195         if(v - diff < m){
31196             diff = v - m;
31197         }else if(v - diff > mx){
31198             diff = mx - v;
31199         }
31200         return diff;
31201     },
31202
31203     // private
31204     onMouseMove : function(e){
31205         
31206         if(this.enabled){
31207             try{// try catch so if something goes wrong the user doesn't get hung
31208
31209             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31210                 return;
31211             }
31212
31213             //var curXY = this.startPoint;
31214             var curSize = this.curSize || this.startBox;
31215             var x = this.startBox.x, y = this.startBox.y;
31216             var ox = x, oy = y;
31217             var w = curSize.width, h = curSize.height;
31218             var ow = w, oh = h;
31219             var mw = this.minWidth, mh = this.minHeight;
31220             var mxw = this.maxWidth, mxh = this.maxHeight;
31221             var wi = this.widthIncrement;
31222             var hi = this.heightIncrement;
31223
31224             var eventXY = e.getXY();
31225             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31226             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31227
31228             var pos = this.activeHandle.position;
31229
31230             switch(pos){
31231                 case "east":
31232                     w += diffX;
31233                     w = Math.min(Math.max(mw, w), mxw);
31234                     break;
31235              
31236                 case "south":
31237                     h += diffY;
31238                     h = Math.min(Math.max(mh, h), mxh);
31239                     break;
31240                 case "southeast":
31241                     w += diffX;
31242                     h += diffY;
31243                     w = Math.min(Math.max(mw, w), mxw);
31244                     h = Math.min(Math.max(mh, h), mxh);
31245                     break;
31246                 case "north":
31247                     diffY = this.constrain(h, diffY, mh, mxh);
31248                     y += diffY;
31249                     h -= diffY;
31250                     break;
31251                 case "hdrag":
31252                     
31253                     if (wi) {
31254                         var adiffX = Math.abs(diffX);
31255                         var sub = (adiffX % wi); // how much 
31256                         if (sub > (wi/2)) { // far enough to snap
31257                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31258                         } else {
31259                             // remove difference.. 
31260                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31261                         }
31262                     }
31263                     x += diffX;
31264                     x = Math.max(this.minX, x);
31265                     break;
31266                 case "west":
31267                     diffX = this.constrain(w, diffX, mw, mxw);
31268                     x += diffX;
31269                     w -= diffX;
31270                     break;
31271                 case "northeast":
31272                     w += diffX;
31273                     w = Math.min(Math.max(mw, w), mxw);
31274                     diffY = this.constrain(h, diffY, mh, mxh);
31275                     y += diffY;
31276                     h -= diffY;
31277                     break;
31278                 case "northwest":
31279                     diffX = this.constrain(w, diffX, mw, mxw);
31280                     diffY = this.constrain(h, diffY, mh, mxh);
31281                     y += diffY;
31282                     h -= diffY;
31283                     x += diffX;
31284                     w -= diffX;
31285                     break;
31286                case "southwest":
31287                     diffX = this.constrain(w, diffX, mw, mxw);
31288                     h += diffY;
31289                     h = Math.min(Math.max(mh, h), mxh);
31290                     x += diffX;
31291                     w -= diffX;
31292                     break;
31293             }
31294
31295             var sw = this.snap(w, wi, mw);
31296             var sh = this.snap(h, hi, mh);
31297             if(sw != w || sh != h){
31298                 switch(pos){
31299                     case "northeast":
31300                         y -= sh - h;
31301                     break;
31302                     case "north":
31303                         y -= sh - h;
31304                         break;
31305                     case "southwest":
31306                         x -= sw - w;
31307                     break;
31308                     case "west":
31309                         x -= sw - w;
31310                         break;
31311                     case "northwest":
31312                         x -= sw - w;
31313                         y -= sh - h;
31314                     break;
31315                 }
31316                 w = sw;
31317                 h = sh;
31318             }
31319
31320             if(this.preserveRatio){
31321                 switch(pos){
31322                     case "southeast":
31323                     case "east":
31324                         h = oh * (w/ow);
31325                         h = Math.min(Math.max(mh, h), mxh);
31326                         w = ow * (h/oh);
31327                        break;
31328                     case "south":
31329                         w = ow * (h/oh);
31330                         w = Math.min(Math.max(mw, w), mxw);
31331                         h = oh * (w/ow);
31332                         break;
31333                     case "northeast":
31334                         w = ow * (h/oh);
31335                         w = Math.min(Math.max(mw, w), mxw);
31336                         h = oh * (w/ow);
31337                     break;
31338                     case "north":
31339                         var tw = w;
31340                         w = ow * (h/oh);
31341                         w = Math.min(Math.max(mw, w), mxw);
31342                         h = oh * (w/ow);
31343                         x += (tw - w) / 2;
31344                         break;
31345                     case "southwest":
31346                         h = oh * (w/ow);
31347                         h = Math.min(Math.max(mh, h), mxh);
31348                         var tw = w;
31349                         w = ow * (h/oh);
31350                         x += tw - w;
31351                         break;
31352                     case "west":
31353                         var th = h;
31354                         h = oh * (w/ow);
31355                         h = Math.min(Math.max(mh, h), mxh);
31356                         y += (th - h) / 2;
31357                         var tw = w;
31358                         w = ow * (h/oh);
31359                         x += tw - w;
31360                        break;
31361                     case "northwest":
31362                         var tw = w;
31363                         var th = h;
31364                         h = oh * (w/ow);
31365                         h = Math.min(Math.max(mh, h), mxh);
31366                         w = ow * (h/oh);
31367                         y += th - h;
31368                         x += tw - w;
31369                        break;
31370
31371                 }
31372             }
31373             if (pos == 'hdrag') {
31374                 w = ow;
31375             }
31376             this.proxy.setBounds(x, y, w, h);
31377             if(this.dynamic){
31378                 this.resizeElement();
31379             }
31380             }catch(e){}
31381         }
31382         this.fireEvent("resizing", this, x, y, w, h, e);
31383     },
31384
31385     // private
31386     handleOver : function(){
31387         if(this.enabled){
31388             this.el.addClass("x-resizable-over");
31389         }
31390     },
31391
31392     // private
31393     handleOut : function(){
31394         if(!this.resizing){
31395             this.el.removeClass("x-resizable-over");
31396         }
31397     },
31398
31399     /**
31400      * Returns the element this component is bound to.
31401      * @return {Roo.Element}
31402      */
31403     getEl : function(){
31404         return this.el;
31405     },
31406
31407     /**
31408      * Returns the resizeChild element (or null).
31409      * @return {Roo.Element}
31410      */
31411     getResizeChild : function(){
31412         return this.resizeChild;
31413     },
31414     groupHandler : function()
31415     {
31416         
31417     },
31418     /**
31419      * Destroys this resizable. If the element was wrapped and
31420      * removeEl is not true then the element remains.
31421      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31422      */
31423     destroy : function(removeEl){
31424         this.proxy.remove();
31425         if(this.overlay){
31426             this.overlay.removeAllListeners();
31427             this.overlay.remove();
31428         }
31429         var ps = Roo.Resizable.positions;
31430         for(var k in ps){
31431             if(typeof ps[k] != "function" && this[ps[k]]){
31432                 var h = this[ps[k]];
31433                 h.el.removeAllListeners();
31434                 h.el.remove();
31435             }
31436         }
31437         if(removeEl){
31438             this.el.update("");
31439             this.el.remove();
31440         }
31441     }
31442 });
31443
31444 // private
31445 // hash to map config positions to true positions
31446 Roo.Resizable.positions = {
31447     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31448     hd: "hdrag"
31449 };
31450
31451 // private
31452 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31453     if(!this.tpl){
31454         // only initialize the template if resizable is used
31455         var tpl = Roo.DomHelper.createTemplate(
31456             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31457         );
31458         tpl.compile();
31459         Roo.Resizable.Handle.prototype.tpl = tpl;
31460     }
31461     this.position = pos;
31462     this.rz = rz;
31463     // show north drag fro topdra
31464     var handlepos = pos == 'hdrag' ? 'north' : pos;
31465     
31466     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31467     if (pos == 'hdrag') {
31468         this.el.setStyle('cursor', 'pointer');
31469     }
31470     this.el.unselectable();
31471     if(transparent){
31472         this.el.setOpacity(0);
31473     }
31474     this.el.on("mousedown", this.onMouseDown, this);
31475     if(!disableTrackOver){
31476         this.el.on("mouseover", this.onMouseOver, this);
31477         this.el.on("mouseout", this.onMouseOut, this);
31478     }
31479 };
31480
31481 // private
31482 Roo.Resizable.Handle.prototype = {
31483     afterResize : function(rz){
31484         Roo.log('after?');
31485         // do nothing
31486     },
31487     // private
31488     onMouseDown : function(e){
31489         this.rz.onMouseDown(this, e);
31490     },
31491     // private
31492     onMouseOver : function(e){
31493         this.rz.handleOver(this, e);
31494     },
31495     // private
31496     onMouseOut : function(e){
31497         this.rz.handleOut(this, e);
31498     }
31499 };/*
31500  * Based on:
31501  * Ext JS Library 1.1.1
31502  * Copyright(c) 2006-2007, Ext JS, LLC.
31503  *
31504  * Originally Released Under LGPL - original licence link has changed is not relivant.
31505  *
31506  * Fork - LGPL
31507  * <script type="text/javascript">
31508  */
31509
31510 /**
31511  * @class Roo.Editor
31512  * @extends Roo.Component
31513  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31514  * @constructor
31515  * Create a new Editor
31516  * @param {Roo.form.Field} field The Field object (or descendant)
31517  * @param {Object} config The config object
31518  */
31519 Roo.Editor = function(field, config){
31520     Roo.Editor.superclass.constructor.call(this, config);
31521     this.field = field;
31522     this.addEvents({
31523         /**
31524              * @event beforestartedit
31525              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31526              * false from the handler of this event.
31527              * @param {Editor} this
31528              * @param {Roo.Element} boundEl The underlying element bound to this editor
31529              * @param {Mixed} value The field value being set
31530              */
31531         "beforestartedit" : true,
31532         /**
31533              * @event startedit
31534              * Fires when this editor is displayed
31535              * @param {Roo.Element} boundEl The underlying element bound to this editor
31536              * @param {Mixed} value The starting field value
31537              */
31538         "startedit" : true,
31539         /**
31540              * @event beforecomplete
31541              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31542              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31543              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31544              * event will not fire since no edit actually occurred.
31545              * @param {Editor} this
31546              * @param {Mixed} value The current field value
31547              * @param {Mixed} startValue The original field value
31548              */
31549         "beforecomplete" : true,
31550         /**
31551              * @event complete
31552              * Fires after editing is complete and any changed value has been written to the underlying field.
31553              * @param {Editor} this
31554              * @param {Mixed} value The current field value
31555              * @param {Mixed} startValue The original field value
31556              */
31557         "complete" : true,
31558         /**
31559          * @event specialkey
31560          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31561          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31562          * @param {Roo.form.Field} this
31563          * @param {Roo.EventObject} e The event object
31564          */
31565         "specialkey" : true
31566     });
31567 };
31568
31569 Roo.extend(Roo.Editor, Roo.Component, {
31570     /**
31571      * @cfg {Boolean/String} autosize
31572      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31573      * or "height" to adopt the height only (defaults to false)
31574      */
31575     /**
31576      * @cfg {Boolean} revertInvalid
31577      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31578      * validation fails (defaults to true)
31579      */
31580     /**
31581      * @cfg {Boolean} ignoreNoChange
31582      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31583      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31584      * will never be ignored.
31585      */
31586     /**
31587      * @cfg {Boolean} hideEl
31588      * False to keep the bound element visible while the editor is displayed (defaults to true)
31589      */
31590     /**
31591      * @cfg {Mixed} value
31592      * The data value of the underlying field (defaults to "")
31593      */
31594     value : "",
31595     /**
31596      * @cfg {String} alignment
31597      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31598      */
31599     alignment: "c-c?",
31600     /**
31601      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31602      * for bottom-right shadow (defaults to "frame")
31603      */
31604     shadow : "frame",
31605     /**
31606      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31607      */
31608     constrain : false,
31609     /**
31610      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31611      */
31612     completeOnEnter : false,
31613     /**
31614      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31615      */
31616     cancelOnEsc : false,
31617     /**
31618      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31619      */
31620     updateEl : false,
31621
31622     // private
31623     onRender : function(ct, position){
31624         this.el = new Roo.Layer({
31625             shadow: this.shadow,
31626             cls: "x-editor",
31627             parentEl : ct,
31628             shim : this.shim,
31629             shadowOffset:4,
31630             id: this.id,
31631             constrain: this.constrain
31632         });
31633         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31634         if(this.field.msgTarget != 'title'){
31635             this.field.msgTarget = 'qtip';
31636         }
31637         this.field.render(this.el);
31638         if(Roo.isGecko){
31639             this.field.el.dom.setAttribute('autocomplete', 'off');
31640         }
31641         this.field.on("specialkey", this.onSpecialKey, this);
31642         if(this.swallowKeys){
31643             this.field.el.swallowEvent(['keydown','keypress']);
31644         }
31645         this.field.show();
31646         this.field.on("blur", this.onBlur, this);
31647         if(this.field.grow){
31648             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31649         }
31650     },
31651
31652     onSpecialKey : function(field, e)
31653     {
31654         //Roo.log('editor onSpecialKey');
31655         if(this.completeOnEnter && e.getKey() == e.ENTER){
31656             e.stopEvent();
31657             this.completeEdit();
31658             return;
31659         }
31660         // do not fire special key otherwise it might hide close the editor...
31661         if(e.getKey() == e.ENTER){    
31662             return;
31663         }
31664         if(this.cancelOnEsc && e.getKey() == e.ESC){
31665             this.cancelEdit();
31666             return;
31667         } 
31668         this.fireEvent('specialkey', field, e);
31669     
31670     },
31671
31672     /**
31673      * Starts the editing process and shows the editor.
31674      * @param {String/HTMLElement/Element} el The element to edit
31675      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31676       * to the innerHTML of el.
31677      */
31678     startEdit : function(el, value){
31679         if(this.editing){
31680             this.completeEdit();
31681         }
31682         this.boundEl = Roo.get(el);
31683         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31684         if(!this.rendered){
31685             this.render(this.parentEl || document.body);
31686         }
31687         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31688             return;
31689         }
31690         this.startValue = v;
31691         this.field.setValue(v);
31692         if(this.autoSize){
31693             var sz = this.boundEl.getSize();
31694             switch(this.autoSize){
31695                 case "width":
31696                 this.setSize(sz.width,  "");
31697                 break;
31698                 case "height":
31699                 this.setSize("",  sz.height);
31700                 break;
31701                 default:
31702                 this.setSize(sz.width,  sz.height);
31703             }
31704         }
31705         this.el.alignTo(this.boundEl, this.alignment);
31706         this.editing = true;
31707         if(Roo.QuickTips){
31708             Roo.QuickTips.disable();
31709         }
31710         this.show();
31711     },
31712
31713     /**
31714      * Sets the height and width of this editor.
31715      * @param {Number} width The new width
31716      * @param {Number} height The new height
31717      */
31718     setSize : function(w, h){
31719         this.field.setSize(w, h);
31720         if(this.el){
31721             this.el.sync();
31722         }
31723     },
31724
31725     /**
31726      * Realigns the editor to the bound field based on the current alignment config value.
31727      */
31728     realign : function(){
31729         this.el.alignTo(this.boundEl, this.alignment);
31730     },
31731
31732     /**
31733      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31734      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31735      */
31736     completeEdit : function(remainVisible){
31737         if(!this.editing){
31738             return;
31739         }
31740         var v = this.getValue();
31741         if(this.revertInvalid !== false && !this.field.isValid()){
31742             v = this.startValue;
31743             this.cancelEdit(true);
31744         }
31745         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31746             this.editing = false;
31747             this.hide();
31748             return;
31749         }
31750         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31751             this.editing = false;
31752             if(this.updateEl && this.boundEl){
31753                 this.boundEl.update(v);
31754             }
31755             if(remainVisible !== true){
31756                 this.hide();
31757             }
31758             this.fireEvent("complete", this, v, this.startValue);
31759         }
31760     },
31761
31762     // private
31763     onShow : function(){
31764         this.el.show();
31765         if(this.hideEl !== false){
31766             this.boundEl.hide();
31767         }
31768         this.field.show();
31769         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31770             this.fixIEFocus = true;
31771             this.deferredFocus.defer(50, this);
31772         }else{
31773             this.field.focus();
31774         }
31775         this.fireEvent("startedit", this.boundEl, this.startValue);
31776     },
31777
31778     deferredFocus : function(){
31779         if(this.editing){
31780             this.field.focus();
31781         }
31782     },
31783
31784     /**
31785      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31786      * reverted to the original starting value.
31787      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31788      * cancel (defaults to false)
31789      */
31790     cancelEdit : function(remainVisible){
31791         if(this.editing){
31792             this.setValue(this.startValue);
31793             if(remainVisible !== true){
31794                 this.hide();
31795             }
31796         }
31797     },
31798
31799     // private
31800     onBlur : function(){
31801         if(this.allowBlur !== true && this.editing){
31802             this.completeEdit();
31803         }
31804     },
31805
31806     // private
31807     onHide : function(){
31808         if(this.editing){
31809             this.completeEdit();
31810             return;
31811         }
31812         this.field.blur();
31813         if(this.field.collapse){
31814             this.field.collapse();
31815         }
31816         this.el.hide();
31817         if(this.hideEl !== false){
31818             this.boundEl.show();
31819         }
31820         if(Roo.QuickTips){
31821             Roo.QuickTips.enable();
31822         }
31823     },
31824
31825     /**
31826      * Sets the data value of the editor
31827      * @param {Mixed} value Any valid value supported by the underlying field
31828      */
31829     setValue : function(v){
31830         this.field.setValue(v);
31831     },
31832
31833     /**
31834      * Gets the data value of the editor
31835      * @return {Mixed} The data value
31836      */
31837     getValue : function(){
31838         return this.field.getValue();
31839     }
31840 });/*
31841  * Based on:
31842  * Ext JS Library 1.1.1
31843  * Copyright(c) 2006-2007, Ext JS, LLC.
31844  *
31845  * Originally Released Under LGPL - original licence link has changed is not relivant.
31846  *
31847  * Fork - LGPL
31848  * <script type="text/javascript">
31849  */
31850  
31851 /**
31852  * @class Roo.BasicDialog
31853  * @extends Roo.util.Observable
31854  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31855  * <pre><code>
31856 var dlg = new Roo.BasicDialog("my-dlg", {
31857     height: 200,
31858     width: 300,
31859     minHeight: 100,
31860     minWidth: 150,
31861     modal: true,
31862     proxyDrag: true,
31863     shadow: true
31864 });
31865 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31866 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31867 dlg.addButton('Cancel', dlg.hide, dlg);
31868 dlg.show();
31869 </code></pre>
31870   <b>A Dialog should always be a direct child of the body element.</b>
31871  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31872  * @cfg {String} title Default text to display in the title bar (defaults to null)
31873  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31874  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31875  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31876  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31877  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31878  * (defaults to null with no animation)
31879  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31880  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31881  * property for valid values (defaults to 'all')
31882  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31883  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31884  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31885  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31886  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31887  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31888  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31889  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31890  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31891  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31892  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31893  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31894  * draggable = true (defaults to false)
31895  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31896  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31897  * shadow (defaults to false)
31898  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31899  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31900  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31901  * @cfg {Array} buttons Array of buttons
31902  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31903  * @constructor
31904  * Create a new BasicDialog.
31905  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31906  * @param {Object} config Configuration options
31907  */
31908 Roo.BasicDialog = function(el, config){
31909     this.el = Roo.get(el);
31910     var dh = Roo.DomHelper;
31911     if(!this.el && config && config.autoCreate){
31912         if(typeof config.autoCreate == "object"){
31913             if(!config.autoCreate.id){
31914                 config.autoCreate.id = el;
31915             }
31916             this.el = dh.append(document.body,
31917                         config.autoCreate, true);
31918         }else{
31919             this.el = dh.append(document.body,
31920                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31921         }
31922     }
31923     el = this.el;
31924     el.setDisplayed(true);
31925     el.hide = this.hideAction;
31926     this.id = el.id;
31927     el.addClass("x-dlg");
31928
31929     Roo.apply(this, config);
31930
31931     this.proxy = el.createProxy("x-dlg-proxy");
31932     this.proxy.hide = this.hideAction;
31933     this.proxy.setOpacity(.5);
31934     this.proxy.hide();
31935
31936     if(config.width){
31937         el.setWidth(config.width);
31938     }
31939     if(config.height){
31940         el.setHeight(config.height);
31941     }
31942     this.size = el.getSize();
31943     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31944         this.xy = [config.x,config.y];
31945     }else{
31946         this.xy = el.getCenterXY(true);
31947     }
31948     /** The header element @type Roo.Element */
31949     this.header = el.child("> .x-dlg-hd");
31950     /** The body element @type Roo.Element */
31951     this.body = el.child("> .x-dlg-bd");
31952     /** The footer element @type Roo.Element */
31953     this.footer = el.child("> .x-dlg-ft");
31954
31955     if(!this.header){
31956         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31957     }
31958     if(!this.body){
31959         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31960     }
31961
31962     this.header.unselectable();
31963     if(this.title){
31964         this.header.update(this.title);
31965     }
31966     // this element allows the dialog to be focused for keyboard event
31967     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31968     this.focusEl.swallowEvent("click", true);
31969
31970     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31971
31972     // wrap the body and footer for special rendering
31973     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31974     if(this.footer){
31975         this.bwrap.dom.appendChild(this.footer.dom);
31976     }
31977
31978     this.bg = this.el.createChild({
31979         tag: "div", cls:"x-dlg-bg",
31980         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31981     });
31982     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31983
31984
31985     if(this.autoScroll !== false && !this.autoTabs){
31986         this.body.setStyle("overflow", "auto");
31987     }
31988
31989     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31990
31991     if(this.closable !== false){
31992         this.el.addClass("x-dlg-closable");
31993         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31994         this.close.on("click", this.closeClick, this);
31995         this.close.addClassOnOver("x-dlg-close-over");
31996     }
31997     if(this.collapsible !== false){
31998         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31999         this.collapseBtn.on("click", this.collapseClick, this);
32000         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32001         this.header.on("dblclick", this.collapseClick, this);
32002     }
32003     if(this.resizable !== false){
32004         this.el.addClass("x-dlg-resizable");
32005         this.resizer = new Roo.Resizable(el, {
32006             minWidth: this.minWidth || 80,
32007             minHeight:this.minHeight || 80,
32008             handles: this.resizeHandles || "all",
32009             pinned: true
32010         });
32011         this.resizer.on("beforeresize", this.beforeResize, this);
32012         this.resizer.on("resize", this.onResize, this);
32013     }
32014     if(this.draggable !== false){
32015         el.addClass("x-dlg-draggable");
32016         if (!this.proxyDrag) {
32017             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32018         }
32019         else {
32020             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32021         }
32022         dd.setHandleElId(this.header.id);
32023         dd.endDrag = this.endMove.createDelegate(this);
32024         dd.startDrag = this.startMove.createDelegate(this);
32025         dd.onDrag = this.onDrag.createDelegate(this);
32026         dd.scroll = false;
32027         this.dd = dd;
32028     }
32029     if(this.modal){
32030         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32031         this.mask.enableDisplayMode("block");
32032         this.mask.hide();
32033         this.el.addClass("x-dlg-modal");
32034     }
32035     if(this.shadow){
32036         this.shadow = new Roo.Shadow({
32037             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32038             offset : this.shadowOffset
32039         });
32040     }else{
32041         this.shadowOffset = 0;
32042     }
32043     if(Roo.useShims && this.shim !== false){
32044         this.shim = this.el.createShim();
32045         this.shim.hide = this.hideAction;
32046         this.shim.hide();
32047     }else{
32048         this.shim = false;
32049     }
32050     if(this.autoTabs){
32051         this.initTabs();
32052     }
32053     if (this.buttons) { 
32054         var bts= this.buttons;
32055         this.buttons = [];
32056         Roo.each(bts, function(b) {
32057             this.addButton(b);
32058         }, this);
32059     }
32060     
32061     
32062     this.addEvents({
32063         /**
32064          * @event keydown
32065          * Fires when a key is pressed
32066          * @param {Roo.BasicDialog} this
32067          * @param {Roo.EventObject} e
32068          */
32069         "keydown" : true,
32070         /**
32071          * @event move
32072          * Fires when this dialog is moved by the user.
32073          * @param {Roo.BasicDialog} this
32074          * @param {Number} x The new page X
32075          * @param {Number} y The new page Y
32076          */
32077         "move" : true,
32078         /**
32079          * @event resize
32080          * Fires when this dialog is resized by the user.
32081          * @param {Roo.BasicDialog} this
32082          * @param {Number} width The new width
32083          * @param {Number} height The new height
32084          */
32085         "resize" : true,
32086         /**
32087          * @event beforehide
32088          * Fires before this dialog is hidden.
32089          * @param {Roo.BasicDialog} this
32090          */
32091         "beforehide" : true,
32092         /**
32093          * @event hide
32094          * Fires when this dialog is hidden.
32095          * @param {Roo.BasicDialog} this
32096          */
32097         "hide" : true,
32098         /**
32099          * @event beforeshow
32100          * Fires before this dialog is shown.
32101          * @param {Roo.BasicDialog} this
32102          */
32103         "beforeshow" : true,
32104         /**
32105          * @event show
32106          * Fires when this dialog is shown.
32107          * @param {Roo.BasicDialog} this
32108          */
32109         "show" : true
32110     });
32111     el.on("keydown", this.onKeyDown, this);
32112     el.on("mousedown", this.toFront, this);
32113     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32114     this.el.hide();
32115     Roo.DialogManager.register(this);
32116     Roo.BasicDialog.superclass.constructor.call(this);
32117 };
32118
32119 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32120     shadowOffset: Roo.isIE ? 6 : 5,
32121     minHeight: 80,
32122     minWidth: 200,
32123     minButtonWidth: 75,
32124     defaultButton: null,
32125     buttonAlign: "right",
32126     tabTag: 'div',
32127     firstShow: true,
32128
32129     /**
32130      * Sets the dialog title text
32131      * @param {String} text The title text to display
32132      * @return {Roo.BasicDialog} this
32133      */
32134     setTitle : function(text){
32135         this.header.update(text);
32136         return this;
32137     },
32138
32139     // private
32140     closeClick : function(){
32141         this.hide();
32142     },
32143
32144     // private
32145     collapseClick : function(){
32146         this[this.collapsed ? "expand" : "collapse"]();
32147     },
32148
32149     /**
32150      * Collapses the dialog to its minimized state (only the title bar is visible).
32151      * Equivalent to the user clicking the collapse dialog button.
32152      */
32153     collapse : function(){
32154         if(!this.collapsed){
32155             this.collapsed = true;
32156             this.el.addClass("x-dlg-collapsed");
32157             this.restoreHeight = this.el.getHeight();
32158             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32159         }
32160     },
32161
32162     /**
32163      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32164      * clicking the expand dialog button.
32165      */
32166     expand : function(){
32167         if(this.collapsed){
32168             this.collapsed = false;
32169             this.el.removeClass("x-dlg-collapsed");
32170             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32171         }
32172     },
32173
32174     /**
32175      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32176      * @return {Roo.TabPanel} The tabs component
32177      */
32178     initTabs : function(){
32179         var tabs = this.getTabs();
32180         while(tabs.getTab(0)){
32181             tabs.removeTab(0);
32182         }
32183         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32184             var dom = el.dom;
32185             tabs.addTab(Roo.id(dom), dom.title);
32186             dom.title = "";
32187         });
32188         tabs.activate(0);
32189         return tabs;
32190     },
32191
32192     // private
32193     beforeResize : function(){
32194         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32195     },
32196
32197     // private
32198     onResize : function(){
32199         this.refreshSize();
32200         this.syncBodyHeight();
32201         this.adjustAssets();
32202         this.focus();
32203         this.fireEvent("resize", this, this.size.width, this.size.height);
32204     },
32205
32206     // private
32207     onKeyDown : function(e){
32208         if(this.isVisible()){
32209             this.fireEvent("keydown", this, e);
32210         }
32211     },
32212
32213     /**
32214      * Resizes the dialog.
32215      * @param {Number} width
32216      * @param {Number} height
32217      * @return {Roo.BasicDialog} this
32218      */
32219     resizeTo : function(width, height){
32220         this.el.setSize(width, height);
32221         this.size = {width: width, height: height};
32222         this.syncBodyHeight();
32223         if(this.fixedcenter){
32224             this.center();
32225         }
32226         if(this.isVisible()){
32227             this.constrainXY();
32228             this.adjustAssets();
32229         }
32230         this.fireEvent("resize", this, width, height);
32231         return this;
32232     },
32233
32234
32235     /**
32236      * Resizes the dialog to fit the specified content size.
32237      * @param {Number} width
32238      * @param {Number} height
32239      * @return {Roo.BasicDialog} this
32240      */
32241     setContentSize : function(w, h){
32242         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32243         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32244         //if(!this.el.isBorderBox()){
32245             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32246             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32247         //}
32248         if(this.tabs){
32249             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32250             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32251         }
32252         this.resizeTo(w, h);
32253         return this;
32254     },
32255
32256     /**
32257      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32258      * executed in response to a particular key being pressed while the dialog is active.
32259      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32260      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32261      * @param {Function} fn The function to call
32262      * @param {Object} scope (optional) The scope of the function
32263      * @return {Roo.BasicDialog} this
32264      */
32265     addKeyListener : function(key, fn, scope){
32266         var keyCode, shift, ctrl, alt;
32267         if(typeof key == "object" && !(key instanceof Array)){
32268             keyCode = key["key"];
32269             shift = key["shift"];
32270             ctrl = key["ctrl"];
32271             alt = key["alt"];
32272         }else{
32273             keyCode = key;
32274         }
32275         var handler = function(dlg, e){
32276             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32277                 var k = e.getKey();
32278                 if(keyCode instanceof Array){
32279                     for(var i = 0, len = keyCode.length; i < len; i++){
32280                         if(keyCode[i] == k){
32281                           fn.call(scope || window, dlg, k, e);
32282                           return;
32283                         }
32284                     }
32285                 }else{
32286                     if(k == keyCode){
32287                         fn.call(scope || window, dlg, k, e);
32288                     }
32289                 }
32290             }
32291         };
32292         this.on("keydown", handler);
32293         return this;
32294     },
32295
32296     /**
32297      * Returns the TabPanel component (creates it if it doesn't exist).
32298      * Note: If you wish to simply check for the existence of tabs without creating them,
32299      * check for a null 'tabs' property.
32300      * @return {Roo.TabPanel} The tabs component
32301      */
32302     getTabs : function(){
32303         if(!this.tabs){
32304             this.el.addClass("x-dlg-auto-tabs");
32305             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32306             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32307         }
32308         return this.tabs;
32309     },
32310
32311     /**
32312      * Adds a button to the footer section of the dialog.
32313      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32314      * object or a valid Roo.DomHelper element config
32315      * @param {Function} handler The function called when the button is clicked
32316      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32317      * @return {Roo.Button} The new button
32318      */
32319     addButton : function(config, handler, scope){
32320         var dh = Roo.DomHelper;
32321         if(!this.footer){
32322             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32323         }
32324         if(!this.btnContainer){
32325             var tb = this.footer.createChild({
32326
32327                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32328                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32329             }, null, true);
32330             this.btnContainer = tb.firstChild.firstChild.firstChild;
32331         }
32332         var bconfig = {
32333             handler: handler,
32334             scope: scope,
32335             minWidth: this.minButtonWidth,
32336             hideParent:true
32337         };
32338         if(typeof config == "string"){
32339             bconfig.text = config;
32340         }else{
32341             if(config.tag){
32342                 bconfig.dhconfig = config;
32343             }else{
32344                 Roo.apply(bconfig, config);
32345             }
32346         }
32347         var fc = false;
32348         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32349             bconfig.position = Math.max(0, bconfig.position);
32350             fc = this.btnContainer.childNodes[bconfig.position];
32351         }
32352          
32353         var btn = new Roo.Button(
32354             fc ? 
32355                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32356                 : this.btnContainer.appendChild(document.createElement("td")),
32357             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32358             bconfig
32359         );
32360         this.syncBodyHeight();
32361         if(!this.buttons){
32362             /**
32363              * Array of all the buttons that have been added to this dialog via addButton
32364              * @type Array
32365              */
32366             this.buttons = [];
32367         }
32368         this.buttons.push(btn);
32369         return btn;
32370     },
32371
32372     /**
32373      * Sets the default button to be focused when the dialog is displayed.
32374      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32375      * @return {Roo.BasicDialog} this
32376      */
32377     setDefaultButton : function(btn){
32378         this.defaultButton = btn;
32379         return this;
32380     },
32381
32382     // private
32383     getHeaderFooterHeight : function(safe){
32384         var height = 0;
32385         if(this.header){
32386            height += this.header.getHeight();
32387         }
32388         if(this.footer){
32389            var fm = this.footer.getMargins();
32390             height += (this.footer.getHeight()+fm.top+fm.bottom);
32391         }
32392         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32393         height += this.centerBg.getPadding("tb");
32394         return height;
32395     },
32396
32397     // private
32398     syncBodyHeight : function()
32399     {
32400         var bd = this.body, // the text
32401             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32402             bw = this.bwrap;
32403         var height = this.size.height - this.getHeaderFooterHeight(false);
32404         bd.setHeight(height-bd.getMargins("tb"));
32405         var hh = this.header.getHeight();
32406         var h = this.size.height-hh;
32407         cb.setHeight(h);
32408         
32409         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32410         bw.setHeight(h-cb.getPadding("tb"));
32411         
32412         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32413         bd.setWidth(bw.getWidth(true));
32414         if(this.tabs){
32415             this.tabs.syncHeight();
32416             if(Roo.isIE){
32417                 this.tabs.el.repaint();
32418             }
32419         }
32420     },
32421
32422     /**
32423      * Restores the previous state of the dialog if Roo.state is configured.
32424      * @return {Roo.BasicDialog} this
32425      */
32426     restoreState : function(){
32427         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32428         if(box && box.width){
32429             this.xy = [box.x, box.y];
32430             this.resizeTo(box.width, box.height);
32431         }
32432         return this;
32433     },
32434
32435     // private
32436     beforeShow : function(){
32437         this.expand();
32438         if(this.fixedcenter){
32439             this.xy = this.el.getCenterXY(true);
32440         }
32441         if(this.modal){
32442             Roo.get(document.body).addClass("x-body-masked");
32443             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32444             this.mask.show();
32445         }
32446         this.constrainXY();
32447     },
32448
32449     // private
32450     animShow : function(){
32451         var b = Roo.get(this.animateTarget).getBox();
32452         this.proxy.setSize(b.width, b.height);
32453         this.proxy.setLocation(b.x, b.y);
32454         this.proxy.show();
32455         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32456                     true, .35, this.showEl.createDelegate(this));
32457     },
32458
32459     /**
32460      * Shows the dialog.
32461      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32462      * @return {Roo.BasicDialog} this
32463      */
32464     show : function(animateTarget){
32465         if (this.fireEvent("beforeshow", this) === false){
32466             return;
32467         }
32468         if(this.syncHeightBeforeShow){
32469             this.syncBodyHeight();
32470         }else if(this.firstShow){
32471             this.firstShow = false;
32472             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32473         }
32474         this.animateTarget = animateTarget || this.animateTarget;
32475         if(!this.el.isVisible()){
32476             this.beforeShow();
32477             if(this.animateTarget && Roo.get(this.animateTarget)){
32478                 this.animShow();
32479             }else{
32480                 this.showEl();
32481             }
32482         }
32483         return this;
32484     },
32485
32486     // private
32487     showEl : function(){
32488         this.proxy.hide();
32489         this.el.setXY(this.xy);
32490         this.el.show();
32491         this.adjustAssets(true);
32492         this.toFront();
32493         this.focus();
32494         // IE peekaboo bug - fix found by Dave Fenwick
32495         if(Roo.isIE){
32496             this.el.repaint();
32497         }
32498         this.fireEvent("show", this);
32499     },
32500
32501     /**
32502      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32503      * dialog itself will receive focus.
32504      */
32505     focus : function(){
32506         if(this.defaultButton){
32507             this.defaultButton.focus();
32508         }else{
32509             this.focusEl.focus();
32510         }
32511     },
32512
32513     // private
32514     constrainXY : function(){
32515         if(this.constraintoviewport !== false){
32516             if(!this.viewSize){
32517                 if(this.container){
32518                     var s = this.container.getSize();
32519                     this.viewSize = [s.width, s.height];
32520                 }else{
32521                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32522                 }
32523             }
32524             var s = Roo.get(this.container||document).getScroll();
32525
32526             var x = this.xy[0], y = this.xy[1];
32527             var w = this.size.width, h = this.size.height;
32528             var vw = this.viewSize[0], vh = this.viewSize[1];
32529             // only move it if it needs it
32530             var moved = false;
32531             // first validate right/bottom
32532             if(x + w > vw+s.left){
32533                 x = vw - w;
32534                 moved = true;
32535             }
32536             if(y + h > vh+s.top){
32537                 y = vh - h;
32538                 moved = true;
32539             }
32540             // then make sure top/left isn't negative
32541             if(x < s.left){
32542                 x = s.left;
32543                 moved = true;
32544             }
32545             if(y < s.top){
32546                 y = s.top;
32547                 moved = true;
32548             }
32549             if(moved){
32550                 // cache xy
32551                 this.xy = [x, y];
32552                 if(this.isVisible()){
32553                     this.el.setLocation(x, y);
32554                     this.adjustAssets();
32555                 }
32556             }
32557         }
32558     },
32559
32560     // private
32561     onDrag : function(){
32562         if(!this.proxyDrag){
32563             this.xy = this.el.getXY();
32564             this.adjustAssets();
32565         }
32566     },
32567
32568     // private
32569     adjustAssets : function(doShow){
32570         var x = this.xy[0], y = this.xy[1];
32571         var w = this.size.width, h = this.size.height;
32572         if(doShow === true){
32573             if(this.shadow){
32574                 this.shadow.show(this.el);
32575             }
32576             if(this.shim){
32577                 this.shim.show();
32578             }
32579         }
32580         if(this.shadow && this.shadow.isVisible()){
32581             this.shadow.show(this.el);
32582         }
32583         if(this.shim && this.shim.isVisible()){
32584             this.shim.setBounds(x, y, w, h);
32585         }
32586     },
32587
32588     // private
32589     adjustViewport : function(w, h){
32590         if(!w || !h){
32591             w = Roo.lib.Dom.getViewWidth();
32592             h = Roo.lib.Dom.getViewHeight();
32593         }
32594         // cache the size
32595         this.viewSize = [w, h];
32596         if(this.modal && this.mask.isVisible()){
32597             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32598             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32599         }
32600         if(this.isVisible()){
32601             this.constrainXY();
32602         }
32603     },
32604
32605     /**
32606      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32607      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32608      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32609      */
32610     destroy : function(removeEl){
32611         if(this.isVisible()){
32612             this.animateTarget = null;
32613             this.hide();
32614         }
32615         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32616         if(this.tabs){
32617             this.tabs.destroy(removeEl);
32618         }
32619         Roo.destroy(
32620              this.shim,
32621              this.proxy,
32622              this.resizer,
32623              this.close,
32624              this.mask
32625         );
32626         if(this.dd){
32627             this.dd.unreg();
32628         }
32629         if(this.buttons){
32630            for(var i = 0, len = this.buttons.length; i < len; i++){
32631                this.buttons[i].destroy();
32632            }
32633         }
32634         this.el.removeAllListeners();
32635         if(removeEl === true){
32636             this.el.update("");
32637             this.el.remove();
32638         }
32639         Roo.DialogManager.unregister(this);
32640     },
32641
32642     // private
32643     startMove : function(){
32644         if(this.proxyDrag){
32645             this.proxy.show();
32646         }
32647         if(this.constraintoviewport !== false){
32648             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32649         }
32650     },
32651
32652     // private
32653     endMove : function(){
32654         if(!this.proxyDrag){
32655             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32656         }else{
32657             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32658             this.proxy.hide();
32659         }
32660         this.refreshSize();
32661         this.adjustAssets();
32662         this.focus();
32663         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32664     },
32665
32666     /**
32667      * Brings this dialog to the front of any other visible dialogs
32668      * @return {Roo.BasicDialog} this
32669      */
32670     toFront : function(){
32671         Roo.DialogManager.bringToFront(this);
32672         return this;
32673     },
32674
32675     /**
32676      * Sends this dialog to the back (under) of any other visible dialogs
32677      * @return {Roo.BasicDialog} this
32678      */
32679     toBack : function(){
32680         Roo.DialogManager.sendToBack(this);
32681         return this;
32682     },
32683
32684     /**
32685      * Centers this dialog in the viewport
32686      * @return {Roo.BasicDialog} this
32687      */
32688     center : function(){
32689         var xy = this.el.getCenterXY(true);
32690         this.moveTo(xy[0], xy[1]);
32691         return this;
32692     },
32693
32694     /**
32695      * Moves the dialog's top-left corner to the specified point
32696      * @param {Number} x
32697      * @param {Number} y
32698      * @return {Roo.BasicDialog} this
32699      */
32700     moveTo : function(x, y){
32701         this.xy = [x,y];
32702         if(this.isVisible()){
32703             this.el.setXY(this.xy);
32704             this.adjustAssets();
32705         }
32706         return this;
32707     },
32708
32709     /**
32710      * Aligns the dialog to the specified element
32711      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32712      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32713      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32714      * @return {Roo.BasicDialog} this
32715      */
32716     alignTo : function(element, position, offsets){
32717         this.xy = this.el.getAlignToXY(element, position, offsets);
32718         if(this.isVisible()){
32719             this.el.setXY(this.xy);
32720             this.adjustAssets();
32721         }
32722         return this;
32723     },
32724
32725     /**
32726      * Anchors an element to another element and realigns it when the window is resized.
32727      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32728      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32729      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32730      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32731      * is a number, it is used as the buffer delay (defaults to 50ms).
32732      * @return {Roo.BasicDialog} this
32733      */
32734     anchorTo : function(el, alignment, offsets, monitorScroll){
32735         var action = function(){
32736             this.alignTo(el, alignment, offsets);
32737         };
32738         Roo.EventManager.onWindowResize(action, this);
32739         var tm = typeof monitorScroll;
32740         if(tm != 'undefined'){
32741             Roo.EventManager.on(window, 'scroll', action, this,
32742                 {buffer: tm == 'number' ? monitorScroll : 50});
32743         }
32744         action.call(this);
32745         return this;
32746     },
32747
32748     /**
32749      * Returns true if the dialog is visible
32750      * @return {Boolean}
32751      */
32752     isVisible : function(){
32753         return this.el.isVisible();
32754     },
32755
32756     // private
32757     animHide : function(callback){
32758         var b = Roo.get(this.animateTarget).getBox();
32759         this.proxy.show();
32760         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32761         this.el.hide();
32762         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32763                     this.hideEl.createDelegate(this, [callback]));
32764     },
32765
32766     /**
32767      * Hides the dialog.
32768      * @param {Function} callback (optional) Function to call when the dialog is hidden
32769      * @return {Roo.BasicDialog} this
32770      */
32771     hide : function(callback){
32772         if (this.fireEvent("beforehide", this) === false){
32773             return;
32774         }
32775         if(this.shadow){
32776             this.shadow.hide();
32777         }
32778         if(this.shim) {
32779           this.shim.hide();
32780         }
32781         // sometimes animateTarget seems to get set.. causing problems...
32782         // this just double checks..
32783         if(this.animateTarget && Roo.get(this.animateTarget)) {
32784            this.animHide(callback);
32785         }else{
32786             this.el.hide();
32787             this.hideEl(callback);
32788         }
32789         return this;
32790     },
32791
32792     // private
32793     hideEl : function(callback){
32794         this.proxy.hide();
32795         if(this.modal){
32796             this.mask.hide();
32797             Roo.get(document.body).removeClass("x-body-masked");
32798         }
32799         this.fireEvent("hide", this);
32800         if(typeof callback == "function"){
32801             callback();
32802         }
32803     },
32804
32805     // private
32806     hideAction : function(){
32807         this.setLeft("-10000px");
32808         this.setTop("-10000px");
32809         this.setStyle("visibility", "hidden");
32810     },
32811
32812     // private
32813     refreshSize : function(){
32814         this.size = this.el.getSize();
32815         this.xy = this.el.getXY();
32816         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32817     },
32818
32819     // private
32820     // z-index is managed by the DialogManager and may be overwritten at any time
32821     setZIndex : function(index){
32822         if(this.modal){
32823             this.mask.setStyle("z-index", index);
32824         }
32825         if(this.shim){
32826             this.shim.setStyle("z-index", ++index);
32827         }
32828         if(this.shadow){
32829             this.shadow.setZIndex(++index);
32830         }
32831         this.el.setStyle("z-index", ++index);
32832         if(this.proxy){
32833             this.proxy.setStyle("z-index", ++index);
32834         }
32835         if(this.resizer){
32836             this.resizer.proxy.setStyle("z-index", ++index);
32837         }
32838
32839         this.lastZIndex = index;
32840     },
32841
32842     /**
32843      * Returns the element for this dialog
32844      * @return {Roo.Element} The underlying dialog Element
32845      */
32846     getEl : function(){
32847         return this.el;
32848     }
32849 });
32850
32851 /**
32852  * @class Roo.DialogManager
32853  * Provides global access to BasicDialogs that have been created and
32854  * support for z-indexing (layering) multiple open dialogs.
32855  */
32856 Roo.DialogManager = function(){
32857     var list = {};
32858     var accessList = [];
32859     var front = null;
32860
32861     // private
32862     var sortDialogs = function(d1, d2){
32863         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32864     };
32865
32866     // private
32867     var orderDialogs = function(){
32868         accessList.sort(sortDialogs);
32869         var seed = Roo.DialogManager.zseed;
32870         for(var i = 0, len = accessList.length; i < len; i++){
32871             var dlg = accessList[i];
32872             if(dlg){
32873                 dlg.setZIndex(seed + (i*10));
32874             }
32875         }
32876     };
32877
32878     return {
32879         /**
32880          * The starting z-index for BasicDialogs (defaults to 9000)
32881          * @type Number The z-index value
32882          */
32883         zseed : 9000,
32884
32885         // private
32886         register : function(dlg){
32887             list[dlg.id] = dlg;
32888             accessList.push(dlg);
32889         },
32890
32891         // private
32892         unregister : function(dlg){
32893             delete list[dlg.id];
32894             var i=0;
32895             var len=0;
32896             if(!accessList.indexOf){
32897                 for(  i = 0, len = accessList.length; i < len; i++){
32898                     if(accessList[i] == dlg){
32899                         accessList.splice(i, 1);
32900                         return;
32901                     }
32902                 }
32903             }else{
32904                  i = accessList.indexOf(dlg);
32905                 if(i != -1){
32906                     accessList.splice(i, 1);
32907                 }
32908             }
32909         },
32910
32911         /**
32912          * Gets a registered dialog by id
32913          * @param {String/Object} id The id of the dialog or a dialog
32914          * @return {Roo.BasicDialog} this
32915          */
32916         get : function(id){
32917             return typeof id == "object" ? id : list[id];
32918         },
32919
32920         /**
32921          * Brings the specified dialog to the front
32922          * @param {String/Object} dlg The id of the dialog or a dialog
32923          * @return {Roo.BasicDialog} this
32924          */
32925         bringToFront : function(dlg){
32926             dlg = this.get(dlg);
32927             if(dlg != front){
32928                 front = dlg;
32929                 dlg._lastAccess = new Date().getTime();
32930                 orderDialogs();
32931             }
32932             return dlg;
32933         },
32934
32935         /**
32936          * Sends the specified dialog to the back
32937          * @param {String/Object} dlg The id of the dialog or a dialog
32938          * @return {Roo.BasicDialog} this
32939          */
32940         sendToBack : function(dlg){
32941             dlg = this.get(dlg);
32942             dlg._lastAccess = -(new Date().getTime());
32943             orderDialogs();
32944             return dlg;
32945         },
32946
32947         /**
32948          * Hides all dialogs
32949          */
32950         hideAll : function(){
32951             for(var id in list){
32952                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32953                     list[id].hide();
32954                 }
32955             }
32956         }
32957     };
32958 }();
32959
32960 /**
32961  * @class Roo.LayoutDialog
32962  * @extends Roo.BasicDialog
32963  * Dialog which provides adjustments for working with a layout in a Dialog.
32964  * Add your necessary layout config options to the dialog's config.<br>
32965  * Example usage (including a nested layout):
32966  * <pre><code>
32967 if(!dialog){
32968     dialog = new Roo.LayoutDialog("download-dlg", {
32969         modal: true,
32970         width:600,
32971         height:450,
32972         shadow:true,
32973         minWidth:500,
32974         minHeight:350,
32975         autoTabs:true,
32976         proxyDrag:true,
32977         // layout config merges with the dialog config
32978         center:{
32979             tabPosition: "top",
32980             alwaysShowTabs: true
32981         }
32982     });
32983     dialog.addKeyListener(27, dialog.hide, dialog);
32984     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32985     dialog.addButton("Build It!", this.getDownload, this);
32986
32987     // we can even add nested layouts
32988     var innerLayout = new Roo.BorderLayout("dl-inner", {
32989         east: {
32990             initialSize: 200,
32991             autoScroll:true,
32992             split:true
32993         },
32994         center: {
32995             autoScroll:true
32996         }
32997     });
32998     innerLayout.beginUpdate();
32999     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33000     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33001     innerLayout.endUpdate(true);
33002
33003     var layout = dialog.getLayout();
33004     layout.beginUpdate();
33005     layout.add("center", new Roo.ContentPanel("standard-panel",
33006                         {title: "Download the Source", fitToFrame:true}));
33007     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33008                {title: "Build your own roo.js"}));
33009     layout.getRegion("center").showPanel(sp);
33010     layout.endUpdate();
33011 }
33012 </code></pre>
33013     * @constructor
33014     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33015     * @param {Object} config configuration options
33016   */
33017 Roo.LayoutDialog = function(el, cfg){
33018     
33019     var config=  cfg;
33020     if (typeof(cfg) == 'undefined') {
33021         config = Roo.apply({}, el);
33022         // not sure why we use documentElement here.. - it should always be body.
33023         // IE7 borks horribly if we use documentElement.
33024         // webkit also does not like documentElement - it creates a body element...
33025         el = Roo.get( document.body || document.documentElement ).createChild();
33026         //config.autoCreate = true;
33027     }
33028     
33029     
33030     config.autoTabs = false;
33031     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33032     this.body.setStyle({overflow:"hidden", position:"relative"});
33033     this.layout = new Roo.BorderLayout(this.body.dom, config);
33034     this.layout.monitorWindowResize = false;
33035     this.el.addClass("x-dlg-auto-layout");
33036     // fix case when center region overwrites center function
33037     this.center = Roo.BasicDialog.prototype.center;
33038     this.on("show", this.layout.layout, this.layout, true);
33039     if (config.items) {
33040         var xitems = config.items;
33041         delete config.items;
33042         Roo.each(xitems, this.addxtype, this);
33043     }
33044     
33045     
33046 };
33047 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33048     /**
33049      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33050      * @deprecated
33051      */
33052     endUpdate : function(){
33053         this.layout.endUpdate();
33054     },
33055
33056     /**
33057      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33058      *  @deprecated
33059      */
33060     beginUpdate : function(){
33061         this.layout.beginUpdate();
33062     },
33063
33064     /**
33065      * Get the BorderLayout for this dialog
33066      * @return {Roo.BorderLayout}
33067      */
33068     getLayout : function(){
33069         return this.layout;
33070     },
33071
33072     showEl : function(){
33073         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33074         if(Roo.isIE7){
33075             this.layout.layout();
33076         }
33077     },
33078
33079     // private
33080     // Use the syncHeightBeforeShow config option to control this automatically
33081     syncBodyHeight : function(){
33082         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33083         if(this.layout){this.layout.layout();}
33084     },
33085     
33086       /**
33087      * Add an xtype element (actually adds to the layout.)
33088      * @return {Object} xdata xtype object data.
33089      */
33090     
33091     addxtype : function(c) {
33092         return this.layout.addxtype(c);
33093     }
33094 });/*
33095  * Based on:
33096  * Ext JS Library 1.1.1
33097  * Copyright(c) 2006-2007, Ext JS, LLC.
33098  *
33099  * Originally Released Under LGPL - original licence link has changed is not relivant.
33100  *
33101  * Fork - LGPL
33102  * <script type="text/javascript">
33103  */
33104  
33105 /**
33106  * @class Roo.MessageBox
33107  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33108  * Example usage:
33109  *<pre><code>
33110 // Basic alert:
33111 Roo.Msg.alert('Status', 'Changes saved successfully.');
33112
33113 // Prompt for user data:
33114 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33115     if (btn == 'ok'){
33116         // process text value...
33117     }
33118 });
33119
33120 // Show a dialog using config options:
33121 Roo.Msg.show({
33122    title:'Save Changes?',
33123    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33124    buttons: Roo.Msg.YESNOCANCEL,
33125    fn: processResult,
33126    animEl: 'elId'
33127 });
33128 </code></pre>
33129  * @singleton
33130  */
33131 Roo.MessageBox = function(){
33132     var dlg, opt, mask, waitTimer;
33133     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33134     var buttons, activeTextEl, bwidth;
33135
33136     // private
33137     var handleButton = function(button){
33138         dlg.hide();
33139         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33140     };
33141
33142     // private
33143     var handleHide = function(){
33144         if(opt && opt.cls){
33145             dlg.el.removeClass(opt.cls);
33146         }
33147         if(waitTimer){
33148             Roo.TaskMgr.stop(waitTimer);
33149             waitTimer = null;
33150         }
33151     };
33152
33153     // private
33154     var updateButtons = function(b){
33155         var width = 0;
33156         if(!b){
33157             buttons["ok"].hide();
33158             buttons["cancel"].hide();
33159             buttons["yes"].hide();
33160             buttons["no"].hide();
33161             dlg.footer.dom.style.display = 'none';
33162             return width;
33163         }
33164         dlg.footer.dom.style.display = '';
33165         for(var k in buttons){
33166             if(typeof buttons[k] != "function"){
33167                 if(b[k]){
33168                     buttons[k].show();
33169                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33170                     width += buttons[k].el.getWidth()+15;
33171                 }else{
33172                     buttons[k].hide();
33173                 }
33174             }
33175         }
33176         return width;
33177     };
33178
33179     // private
33180     var handleEsc = function(d, k, e){
33181         if(opt && opt.closable !== false){
33182             dlg.hide();
33183         }
33184         if(e){
33185             e.stopEvent();
33186         }
33187     };
33188
33189     return {
33190         /**
33191          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33192          * @return {Roo.BasicDialog} The BasicDialog element
33193          */
33194         getDialog : function(){
33195            if(!dlg){
33196                 dlg = new Roo.BasicDialog("x-msg-box", {
33197                     autoCreate : true,
33198                     shadow: true,
33199                     draggable: true,
33200                     resizable:false,
33201                     constraintoviewport:false,
33202                     fixedcenter:true,
33203                     collapsible : false,
33204                     shim:true,
33205                     modal: true,
33206                     width:400, height:100,
33207                     buttonAlign:"center",
33208                     closeClick : function(){
33209                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33210                             handleButton("no");
33211                         }else{
33212                             handleButton("cancel");
33213                         }
33214                     }
33215                 });
33216                 dlg.on("hide", handleHide);
33217                 mask = dlg.mask;
33218                 dlg.addKeyListener(27, handleEsc);
33219                 buttons = {};
33220                 var bt = this.buttonText;
33221                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33222                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33223                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33224                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33225                 bodyEl = dlg.body.createChild({
33226
33227                     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>'
33228                 });
33229                 msgEl = bodyEl.dom.firstChild;
33230                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33231                 textboxEl.enableDisplayMode();
33232                 textboxEl.addKeyListener([10,13], function(){
33233                     if(dlg.isVisible() && opt && opt.buttons){
33234                         if(opt.buttons.ok){
33235                             handleButton("ok");
33236                         }else if(opt.buttons.yes){
33237                             handleButton("yes");
33238                         }
33239                     }
33240                 });
33241                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33242                 textareaEl.enableDisplayMode();
33243                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33244                 progressEl.enableDisplayMode();
33245                 var pf = progressEl.dom.firstChild;
33246                 if (pf) {
33247                     pp = Roo.get(pf.firstChild);
33248                     pp.setHeight(pf.offsetHeight);
33249                 }
33250                 
33251             }
33252             return dlg;
33253         },
33254
33255         /**
33256          * Updates the message box body text
33257          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33258          * the XHTML-compliant non-breaking space character '&amp;#160;')
33259          * @return {Roo.MessageBox} This message box
33260          */
33261         updateText : function(text){
33262             if(!dlg.isVisible() && !opt.width){
33263                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33264             }
33265             msgEl.innerHTML = text || '&#160;';
33266       
33267             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33268             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33269             var w = Math.max(
33270                     Math.min(opt.width || cw , this.maxWidth), 
33271                     Math.max(opt.minWidth || this.minWidth, bwidth)
33272             );
33273             if(opt.prompt){
33274                 activeTextEl.setWidth(w);
33275             }
33276             if(dlg.isVisible()){
33277                 dlg.fixedcenter = false;
33278             }
33279             // to big, make it scroll. = But as usual stupid IE does not support
33280             // !important..
33281             
33282             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33283                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33284                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33285             } else {
33286                 bodyEl.dom.style.height = '';
33287                 bodyEl.dom.style.overflowY = '';
33288             }
33289             if (cw > w) {
33290                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33291             } else {
33292                 bodyEl.dom.style.overflowX = '';
33293             }
33294             
33295             dlg.setContentSize(w, bodyEl.getHeight());
33296             if(dlg.isVisible()){
33297                 dlg.fixedcenter = true;
33298             }
33299             return this;
33300         },
33301
33302         /**
33303          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33304          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33305          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33306          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33307          * @return {Roo.MessageBox} This message box
33308          */
33309         updateProgress : function(value, text){
33310             if(text){
33311                 this.updateText(text);
33312             }
33313             if (pp) { // weird bug on my firefox - for some reason this is not defined
33314                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33315             }
33316             return this;
33317         },        
33318
33319         /**
33320          * Returns true if the message box is currently displayed
33321          * @return {Boolean} True if the message box is visible, else false
33322          */
33323         isVisible : function(){
33324             return dlg && dlg.isVisible();  
33325         },
33326
33327         /**
33328          * Hides the message box if it is displayed
33329          */
33330         hide : function(){
33331             if(this.isVisible()){
33332                 dlg.hide();
33333             }  
33334         },
33335
33336         /**
33337          * Displays a new message box, or reinitializes an existing message box, based on the config options
33338          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33339          * The following config object properties are supported:
33340          * <pre>
33341 Property    Type             Description
33342 ----------  ---------------  ------------------------------------------------------------------------------------
33343 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33344                                    closes (defaults to undefined)
33345 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33346                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33347 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33348                                    progress and wait dialogs will ignore this property and always hide the
33349                                    close button as they can only be closed programmatically.
33350 cls               String           A custom CSS class to apply to the message box element
33351 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33352                                    displayed (defaults to 75)
33353 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33354                                    function will be btn (the name of the button that was clicked, if applicable,
33355                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33356                                    Progress and wait dialogs will ignore this option since they do not respond to
33357                                    user actions and can only be closed programmatically, so any required function
33358                                    should be called by the same code after it closes the dialog.
33359 icon              String           A CSS class that provides a background image to be used as an icon for
33360                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33361 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33362 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33363 modal             Boolean          False to allow user interaction with the page while the message box is
33364                                    displayed (defaults to true)
33365 msg               String           A string that will replace the existing message box body text (defaults
33366                                    to the XHTML-compliant non-breaking space character '&#160;')
33367 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33368 progress          Boolean          True to display a progress bar (defaults to false)
33369 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33370 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33371 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33372 title             String           The title text
33373 value             String           The string value to set into the active textbox element if displayed
33374 wait              Boolean          True to display a progress bar (defaults to false)
33375 width             Number           The width of the dialog in pixels
33376 </pre>
33377          *
33378          * Example usage:
33379          * <pre><code>
33380 Roo.Msg.show({
33381    title: 'Address',
33382    msg: 'Please enter your address:',
33383    width: 300,
33384    buttons: Roo.MessageBox.OKCANCEL,
33385    multiline: true,
33386    fn: saveAddress,
33387    animEl: 'addAddressBtn'
33388 });
33389 </code></pre>
33390          * @param {Object} config Configuration options
33391          * @return {Roo.MessageBox} This message box
33392          */
33393         show : function(options)
33394         {
33395             
33396             // this causes nightmares if you show one dialog after another
33397             // especially on callbacks..
33398              
33399             if(this.isVisible()){
33400                 
33401                 this.hide();
33402                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33403                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33404                 Roo.log("New Dialog Message:" +  options.msg )
33405                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33406                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33407                 
33408             }
33409             var d = this.getDialog();
33410             opt = options;
33411             d.setTitle(opt.title || "&#160;");
33412             d.close.setDisplayed(opt.closable !== false);
33413             activeTextEl = textboxEl;
33414             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33415             if(opt.prompt){
33416                 if(opt.multiline){
33417                     textboxEl.hide();
33418                     textareaEl.show();
33419                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33420                         opt.multiline : this.defaultTextHeight);
33421                     activeTextEl = textareaEl;
33422                 }else{
33423                     textboxEl.show();
33424                     textareaEl.hide();
33425                 }
33426             }else{
33427                 textboxEl.hide();
33428                 textareaEl.hide();
33429             }
33430             progressEl.setDisplayed(opt.progress === true);
33431             this.updateProgress(0);
33432             activeTextEl.dom.value = opt.value || "";
33433             if(opt.prompt){
33434                 dlg.setDefaultButton(activeTextEl);
33435             }else{
33436                 var bs = opt.buttons;
33437                 var db = null;
33438                 if(bs && bs.ok){
33439                     db = buttons["ok"];
33440                 }else if(bs && bs.yes){
33441                     db = buttons["yes"];
33442                 }
33443                 dlg.setDefaultButton(db);
33444             }
33445             bwidth = updateButtons(opt.buttons);
33446             this.updateText(opt.msg);
33447             if(opt.cls){
33448                 d.el.addClass(opt.cls);
33449             }
33450             d.proxyDrag = opt.proxyDrag === true;
33451             d.modal = opt.modal !== false;
33452             d.mask = opt.modal !== false ? mask : false;
33453             if(!d.isVisible()){
33454                 // force it to the end of the z-index stack so it gets a cursor in FF
33455                 document.body.appendChild(dlg.el.dom);
33456                 d.animateTarget = null;
33457                 d.show(options.animEl);
33458             }
33459             return this;
33460         },
33461
33462         /**
33463          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33464          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33465          * and closing the message box when the process is complete.
33466          * @param {String} title The title bar text
33467          * @param {String} msg The message box body text
33468          * @return {Roo.MessageBox} This message box
33469          */
33470         progress : function(title, msg){
33471             this.show({
33472                 title : title,
33473                 msg : msg,
33474                 buttons: false,
33475                 progress:true,
33476                 closable:false,
33477                 minWidth: this.minProgressWidth,
33478                 modal : true
33479             });
33480             return this;
33481         },
33482
33483         /**
33484          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33485          * If a callback function is passed it will be called after the user clicks the button, and the
33486          * id of the button that was clicked will be passed as the only parameter to the callback
33487          * (could also be the top-right close button).
33488          * @param {String} title The title bar text
33489          * @param {String} msg The message box body text
33490          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33491          * @param {Object} scope (optional) The scope of the callback function
33492          * @return {Roo.MessageBox} This message box
33493          */
33494         alert : function(title, msg, fn, scope){
33495             this.show({
33496                 title : title,
33497                 msg : msg,
33498                 buttons: this.OK,
33499                 fn: fn,
33500                 scope : scope,
33501                 modal : true
33502             });
33503             return this;
33504         },
33505
33506         /**
33507          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33508          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33509          * You are responsible for closing the message box when the process is complete.
33510          * @param {String} msg The message box body text
33511          * @param {String} title (optional) The title bar text
33512          * @return {Roo.MessageBox} This message box
33513          */
33514         wait : function(msg, title){
33515             this.show({
33516                 title : title,
33517                 msg : msg,
33518                 buttons: false,
33519                 closable:false,
33520                 progress:true,
33521                 modal:true,
33522                 width:300,
33523                 wait:true
33524             });
33525             waitTimer = Roo.TaskMgr.start({
33526                 run: function(i){
33527                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33528                 },
33529                 interval: 1000
33530             });
33531             return this;
33532         },
33533
33534         /**
33535          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33536          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33537          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33538          * @param {String} title The title bar text
33539          * @param {String} msg The message box body text
33540          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33541          * @param {Object} scope (optional) The scope of the callback function
33542          * @return {Roo.MessageBox} This message box
33543          */
33544         confirm : function(title, msg, fn, scope){
33545             this.show({
33546                 title : title,
33547                 msg : msg,
33548                 buttons: this.YESNO,
33549                 fn: fn,
33550                 scope : scope,
33551                 modal : true
33552             });
33553             return this;
33554         },
33555
33556         /**
33557          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33558          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33559          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33560          * (could also be the top-right close button) and the text that was entered will be passed as the two
33561          * parameters to the callback.
33562          * @param {String} title The title bar text
33563          * @param {String} msg The message box body text
33564          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33565          * @param {Object} scope (optional) The scope of the callback function
33566          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33567          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33568          * @return {Roo.MessageBox} This message box
33569          */
33570         prompt : function(title, msg, fn, scope, multiline){
33571             this.show({
33572                 title : title,
33573                 msg : msg,
33574                 buttons: this.OKCANCEL,
33575                 fn: fn,
33576                 minWidth:250,
33577                 scope : scope,
33578                 prompt:true,
33579                 multiline: multiline,
33580                 modal : true
33581             });
33582             return this;
33583         },
33584
33585         /**
33586          * Button config that displays a single OK button
33587          * @type Object
33588          */
33589         OK : {ok:true},
33590         /**
33591          * Button config that displays Yes and No buttons
33592          * @type Object
33593          */
33594         YESNO : {yes:true, no:true},
33595         /**
33596          * Button config that displays OK and Cancel buttons
33597          * @type Object
33598          */
33599         OKCANCEL : {ok:true, cancel:true},
33600         /**
33601          * Button config that displays Yes, No and Cancel buttons
33602          * @type Object
33603          */
33604         YESNOCANCEL : {yes:true, no:true, cancel:true},
33605
33606         /**
33607          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33608          * @type Number
33609          */
33610         defaultTextHeight : 75,
33611         /**
33612          * The maximum width in pixels of the message box (defaults to 600)
33613          * @type Number
33614          */
33615         maxWidth : 600,
33616         /**
33617          * The minimum width in pixels of the message box (defaults to 100)
33618          * @type Number
33619          */
33620         minWidth : 100,
33621         /**
33622          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33623          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33624          * @type Number
33625          */
33626         minProgressWidth : 250,
33627         /**
33628          * An object containing the default button text strings that can be overriden for localized language support.
33629          * Supported properties are: ok, cancel, yes and no.
33630          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33631          * @type Object
33632          */
33633         buttonText : {
33634             ok : "OK",
33635             cancel : "Cancel",
33636             yes : "Yes",
33637             no : "No"
33638         }
33639     };
33640 }();
33641
33642 /**
33643  * Shorthand for {@link Roo.MessageBox}
33644  */
33645 Roo.Msg = Roo.MessageBox;/*
33646  * Based on:
33647  * Ext JS Library 1.1.1
33648  * Copyright(c) 2006-2007, Ext JS, LLC.
33649  *
33650  * Originally Released Under LGPL - original licence link has changed is not relivant.
33651  *
33652  * Fork - LGPL
33653  * <script type="text/javascript">
33654  */
33655 /**
33656  * @class Roo.QuickTips
33657  * Provides attractive and customizable tooltips for any element.
33658  * @singleton
33659  */
33660 Roo.QuickTips = function(){
33661     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33662     var ce, bd, xy, dd;
33663     var visible = false, disabled = true, inited = false;
33664     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33665     
33666     var onOver = function(e){
33667         if(disabled){
33668             return;
33669         }
33670         var t = e.getTarget();
33671         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33672             return;
33673         }
33674         if(ce && t == ce.el){
33675             clearTimeout(hideProc);
33676             return;
33677         }
33678         if(t && tagEls[t.id]){
33679             tagEls[t.id].el = t;
33680             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33681             return;
33682         }
33683         var ttp, et = Roo.fly(t);
33684         var ns = cfg.namespace;
33685         if(tm.interceptTitles && t.title){
33686             ttp = t.title;
33687             t.qtip = ttp;
33688             t.removeAttribute("title");
33689             e.preventDefault();
33690         }else{
33691             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33692         }
33693         if(ttp){
33694             showProc = show.defer(tm.showDelay, tm, [{
33695                 el: t, 
33696                 text: ttp.replace(/\\n/g,'<br/>'),
33697                 width: et.getAttributeNS(ns, cfg.width),
33698                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33699                 title: et.getAttributeNS(ns, cfg.title),
33700                     cls: et.getAttributeNS(ns, cfg.cls)
33701             }]);
33702         }
33703     };
33704     
33705     var onOut = function(e){
33706         clearTimeout(showProc);
33707         var t = e.getTarget();
33708         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33709             hideProc = setTimeout(hide, tm.hideDelay);
33710         }
33711     };
33712     
33713     var onMove = function(e){
33714         if(disabled){
33715             return;
33716         }
33717         xy = e.getXY();
33718         xy[1] += 18;
33719         if(tm.trackMouse && ce){
33720             el.setXY(xy);
33721         }
33722     };
33723     
33724     var onDown = function(e){
33725         clearTimeout(showProc);
33726         clearTimeout(hideProc);
33727         if(!e.within(el)){
33728             if(tm.hideOnClick){
33729                 hide();
33730                 tm.disable();
33731                 tm.enable.defer(100, tm);
33732             }
33733         }
33734     };
33735     
33736     var getPad = function(){
33737         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33738     };
33739
33740     var show = function(o){
33741         if(disabled){
33742             return;
33743         }
33744         clearTimeout(dismissProc);
33745         ce = o;
33746         if(removeCls){ // in case manually hidden
33747             el.removeClass(removeCls);
33748             removeCls = null;
33749         }
33750         if(ce.cls){
33751             el.addClass(ce.cls);
33752             removeCls = ce.cls;
33753         }
33754         if(ce.title){
33755             tipTitle.update(ce.title);
33756             tipTitle.show();
33757         }else{
33758             tipTitle.update('');
33759             tipTitle.hide();
33760         }
33761         el.dom.style.width  = tm.maxWidth+'px';
33762         //tipBody.dom.style.width = '';
33763         tipBodyText.update(o.text);
33764         var p = getPad(), w = ce.width;
33765         if(!w){
33766             var td = tipBodyText.dom;
33767             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33768             if(aw > tm.maxWidth){
33769                 w = tm.maxWidth;
33770             }else if(aw < tm.minWidth){
33771                 w = tm.minWidth;
33772             }else{
33773                 w = aw;
33774             }
33775         }
33776         //tipBody.setWidth(w);
33777         el.setWidth(parseInt(w, 10) + p);
33778         if(ce.autoHide === false){
33779             close.setDisplayed(true);
33780             if(dd){
33781                 dd.unlock();
33782             }
33783         }else{
33784             close.setDisplayed(false);
33785             if(dd){
33786                 dd.lock();
33787             }
33788         }
33789         if(xy){
33790             el.avoidY = xy[1]-18;
33791             el.setXY(xy);
33792         }
33793         if(tm.animate){
33794             el.setOpacity(.1);
33795             el.setStyle("visibility", "visible");
33796             el.fadeIn({callback: afterShow});
33797         }else{
33798             afterShow();
33799         }
33800     };
33801     
33802     var afterShow = function(){
33803         if(ce){
33804             el.show();
33805             esc.enable();
33806             if(tm.autoDismiss && ce.autoHide !== false){
33807                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33808             }
33809         }
33810     };
33811     
33812     var hide = function(noanim){
33813         clearTimeout(dismissProc);
33814         clearTimeout(hideProc);
33815         ce = null;
33816         if(el.isVisible()){
33817             esc.disable();
33818             if(noanim !== true && tm.animate){
33819                 el.fadeOut({callback: afterHide});
33820             }else{
33821                 afterHide();
33822             } 
33823         }
33824     };
33825     
33826     var afterHide = function(){
33827         el.hide();
33828         if(removeCls){
33829             el.removeClass(removeCls);
33830             removeCls = null;
33831         }
33832     };
33833     
33834     return {
33835         /**
33836         * @cfg {Number} minWidth
33837         * The minimum width of the quick tip (defaults to 40)
33838         */
33839        minWidth : 40,
33840         /**
33841         * @cfg {Number} maxWidth
33842         * The maximum width of the quick tip (defaults to 300)
33843         */
33844        maxWidth : 300,
33845         /**
33846         * @cfg {Boolean} interceptTitles
33847         * True to automatically use the element's DOM title value if available (defaults to false)
33848         */
33849        interceptTitles : false,
33850         /**
33851         * @cfg {Boolean} trackMouse
33852         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33853         */
33854        trackMouse : false,
33855         /**
33856         * @cfg {Boolean} hideOnClick
33857         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33858         */
33859        hideOnClick : true,
33860         /**
33861         * @cfg {Number} showDelay
33862         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33863         */
33864        showDelay : 500,
33865         /**
33866         * @cfg {Number} hideDelay
33867         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33868         */
33869        hideDelay : 200,
33870         /**
33871         * @cfg {Boolean} autoHide
33872         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33873         * Used in conjunction with hideDelay.
33874         */
33875        autoHide : true,
33876         /**
33877         * @cfg {Boolean}
33878         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33879         * (defaults to true).  Used in conjunction with autoDismissDelay.
33880         */
33881        autoDismiss : true,
33882         /**
33883         * @cfg {Number}
33884         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33885         */
33886        autoDismissDelay : 5000,
33887        /**
33888         * @cfg {Boolean} animate
33889         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33890         */
33891        animate : false,
33892
33893        /**
33894         * @cfg {String} title
33895         * Title text to display (defaults to '').  This can be any valid HTML markup.
33896         */
33897         title: '',
33898        /**
33899         * @cfg {String} text
33900         * Body text to display (defaults to '').  This can be any valid HTML markup.
33901         */
33902         text : '',
33903        /**
33904         * @cfg {String} cls
33905         * A CSS class to apply to the base quick tip element (defaults to '').
33906         */
33907         cls : '',
33908        /**
33909         * @cfg {Number} width
33910         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33911         * minWidth or maxWidth.
33912         */
33913         width : null,
33914
33915     /**
33916      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33917      * or display QuickTips in a page.
33918      */
33919        init : function(){
33920           tm = Roo.QuickTips;
33921           cfg = tm.tagConfig;
33922           if(!inited){
33923               if(!Roo.isReady){ // allow calling of init() before onReady
33924                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33925                   return;
33926               }
33927               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33928               el.fxDefaults = {stopFx: true};
33929               // maximum custom styling
33930               //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>');
33931               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>');              
33932               tipTitle = el.child('h3');
33933               tipTitle.enableDisplayMode("block");
33934               tipBody = el.child('div.x-tip-bd');
33935               tipBodyText = el.child('div.x-tip-bd-inner');
33936               //bdLeft = el.child('div.x-tip-bd-left');
33937               //bdRight = el.child('div.x-tip-bd-right');
33938               close = el.child('div.x-tip-close');
33939               close.enableDisplayMode("block");
33940               close.on("click", hide);
33941               var d = Roo.get(document);
33942               d.on("mousedown", onDown);
33943               d.on("mouseover", onOver);
33944               d.on("mouseout", onOut);
33945               d.on("mousemove", onMove);
33946               esc = d.addKeyListener(27, hide);
33947               esc.disable();
33948               if(Roo.dd.DD){
33949                   dd = el.initDD("default", null, {
33950                       onDrag : function(){
33951                           el.sync();  
33952                       }
33953                   });
33954                   dd.setHandleElId(tipTitle.id);
33955                   dd.lock();
33956               }
33957               inited = true;
33958           }
33959           this.enable(); 
33960        },
33961
33962     /**
33963      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33964      * are supported:
33965      * <pre>
33966 Property    Type                   Description
33967 ----------  ---------------------  ------------------------------------------------------------------------
33968 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33969      * </ul>
33970      * @param {Object} config The config object
33971      */
33972        register : function(config){
33973            var cs = config instanceof Array ? config : arguments;
33974            for(var i = 0, len = cs.length; i < len; i++) {
33975                var c = cs[i];
33976                var target = c.target;
33977                if(target){
33978                    if(target instanceof Array){
33979                        for(var j = 0, jlen = target.length; j < jlen; j++){
33980                            tagEls[target[j]] = c;
33981                        }
33982                    }else{
33983                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33984                    }
33985                }
33986            }
33987        },
33988
33989     /**
33990      * Removes this quick tip from its element and destroys it.
33991      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33992      */
33993        unregister : function(el){
33994            delete tagEls[Roo.id(el)];
33995        },
33996
33997     /**
33998      * Enable this quick tip.
33999      */
34000        enable : function(){
34001            if(inited && disabled){
34002                locks.pop();
34003                if(locks.length < 1){
34004                    disabled = false;
34005                }
34006            }
34007        },
34008
34009     /**
34010      * Disable this quick tip.
34011      */
34012        disable : function(){
34013           disabled = true;
34014           clearTimeout(showProc);
34015           clearTimeout(hideProc);
34016           clearTimeout(dismissProc);
34017           if(ce){
34018               hide(true);
34019           }
34020           locks.push(1);
34021        },
34022
34023     /**
34024      * Returns true if the quick tip is enabled, else false.
34025      */
34026        isEnabled : function(){
34027             return !disabled;
34028        },
34029
34030         // private
34031        tagConfig : {
34032            namespace : "roo", // was ext?? this may break..
34033            alt_namespace : "ext",
34034            attribute : "qtip",
34035            width : "width",
34036            target : "target",
34037            title : "qtitle",
34038            hide : "hide",
34039            cls : "qclass"
34040        }
34041    };
34042 }();
34043
34044 // backwards compat
34045 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34046  * Based on:
34047  * Ext JS Library 1.1.1
34048  * Copyright(c) 2006-2007, Ext JS, LLC.
34049  *
34050  * Originally Released Under LGPL - original licence link has changed is not relivant.
34051  *
34052  * Fork - LGPL
34053  * <script type="text/javascript">
34054  */
34055  
34056
34057 /**
34058  * @class Roo.tree.TreePanel
34059  * @extends Roo.data.Tree
34060
34061  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34062  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34063  * @cfg {Boolean} enableDD true to enable drag and drop
34064  * @cfg {Boolean} enableDrag true to enable just drag
34065  * @cfg {Boolean} enableDrop true to enable just drop
34066  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34067  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34068  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34069  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34070  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34071  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34072  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34073  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34074  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34075  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34076  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34077  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34078  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34079  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34080  * @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>
34081  * @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>
34082  * 
34083  * @constructor
34084  * @param {String/HTMLElement/Element} el The container element
34085  * @param {Object} config
34086  */
34087 Roo.tree.TreePanel = function(el, config){
34088     var root = false;
34089     var loader = false;
34090     if (config.root) {
34091         root = config.root;
34092         delete config.root;
34093     }
34094     if (config.loader) {
34095         loader = config.loader;
34096         delete config.loader;
34097     }
34098     
34099     Roo.apply(this, config);
34100     Roo.tree.TreePanel.superclass.constructor.call(this);
34101     this.el = Roo.get(el);
34102     this.el.addClass('x-tree');
34103     //console.log(root);
34104     if (root) {
34105         this.setRootNode( Roo.factory(root, Roo.tree));
34106     }
34107     if (loader) {
34108         this.loader = Roo.factory(loader, Roo.tree);
34109     }
34110    /**
34111     * Read-only. The id of the container element becomes this TreePanel's id.
34112     */
34113     this.id = this.el.id;
34114     this.addEvents({
34115         /**
34116         * @event beforeload
34117         * Fires before a node is loaded, return false to cancel
34118         * @param {Node} node The node being loaded
34119         */
34120         "beforeload" : true,
34121         /**
34122         * @event load
34123         * Fires when a node is loaded
34124         * @param {Node} node The node that was loaded
34125         */
34126         "load" : true,
34127         /**
34128         * @event textchange
34129         * Fires when the text for a node is changed
34130         * @param {Node} node The node
34131         * @param {String} text The new text
34132         * @param {String} oldText The old text
34133         */
34134         "textchange" : true,
34135         /**
34136         * @event beforeexpand
34137         * Fires before a node is expanded, return false to cancel.
34138         * @param {Node} node The node
34139         * @param {Boolean} deep
34140         * @param {Boolean} anim
34141         */
34142         "beforeexpand" : true,
34143         /**
34144         * @event beforecollapse
34145         * Fires before a node is collapsed, return false to cancel.
34146         * @param {Node} node The node
34147         * @param {Boolean} deep
34148         * @param {Boolean} anim
34149         */
34150         "beforecollapse" : true,
34151         /**
34152         * @event expand
34153         * Fires when a node is expanded
34154         * @param {Node} node The node
34155         */
34156         "expand" : true,
34157         /**
34158         * @event disabledchange
34159         * Fires when the disabled status of a node changes
34160         * @param {Node} node The node
34161         * @param {Boolean} disabled
34162         */
34163         "disabledchange" : true,
34164         /**
34165         * @event collapse
34166         * Fires when a node is collapsed
34167         * @param {Node} node The node
34168         */
34169         "collapse" : true,
34170         /**
34171         * @event beforeclick
34172         * Fires before click processing on a node. Return false to cancel the default action.
34173         * @param {Node} node The node
34174         * @param {Roo.EventObject} e The event object
34175         */
34176         "beforeclick":true,
34177         /**
34178         * @event checkchange
34179         * Fires when a node with a checkbox's checked property changes
34180         * @param {Node} this This node
34181         * @param {Boolean} checked
34182         */
34183         "checkchange":true,
34184         /**
34185         * @event click
34186         * Fires when a node is clicked
34187         * @param {Node} node The node
34188         * @param {Roo.EventObject} e The event object
34189         */
34190         "click":true,
34191         /**
34192         * @event dblclick
34193         * Fires when a node is double clicked
34194         * @param {Node} node The node
34195         * @param {Roo.EventObject} e The event object
34196         */
34197         "dblclick":true,
34198         /**
34199         * @event contextmenu
34200         * Fires when a node is right clicked
34201         * @param {Node} node The node
34202         * @param {Roo.EventObject} e The event object
34203         */
34204         "contextmenu":true,
34205         /**
34206         * @event beforechildrenrendered
34207         * Fires right before the child nodes for a node are rendered
34208         * @param {Node} node The node
34209         */
34210         "beforechildrenrendered":true,
34211         /**
34212         * @event startdrag
34213         * Fires when a node starts being dragged
34214         * @param {Roo.tree.TreePanel} this
34215         * @param {Roo.tree.TreeNode} node
34216         * @param {event} e The raw browser event
34217         */ 
34218        "startdrag" : true,
34219        /**
34220         * @event enddrag
34221         * Fires when a drag operation is complete
34222         * @param {Roo.tree.TreePanel} this
34223         * @param {Roo.tree.TreeNode} node
34224         * @param {event} e The raw browser event
34225         */
34226        "enddrag" : true,
34227        /**
34228         * @event dragdrop
34229         * Fires when a dragged node is dropped on a valid DD target
34230         * @param {Roo.tree.TreePanel} this
34231         * @param {Roo.tree.TreeNode} node
34232         * @param {DD} dd The dd it was dropped on
34233         * @param {event} e The raw browser event
34234         */
34235        "dragdrop" : true,
34236        /**
34237         * @event beforenodedrop
34238         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34239         * passed to handlers has the following properties:<br />
34240         * <ul style="padding:5px;padding-left:16px;">
34241         * <li>tree - The TreePanel</li>
34242         * <li>target - The node being targeted for the drop</li>
34243         * <li>data - The drag data from the drag source</li>
34244         * <li>point - The point of the drop - append, above or below</li>
34245         * <li>source - The drag source</li>
34246         * <li>rawEvent - Raw mouse event</li>
34247         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34248         * to be inserted by setting them on this object.</li>
34249         * <li>cancel - Set this to true to cancel the drop.</li>
34250         * </ul>
34251         * @param {Object} dropEvent
34252         */
34253        "beforenodedrop" : true,
34254        /**
34255         * @event nodedrop
34256         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34257         * passed to handlers has the following properties:<br />
34258         * <ul style="padding:5px;padding-left:16px;">
34259         * <li>tree - The TreePanel</li>
34260         * <li>target - The node being targeted for the drop</li>
34261         * <li>data - The drag data from the drag source</li>
34262         * <li>point - The point of the drop - append, above or below</li>
34263         * <li>source - The drag source</li>
34264         * <li>rawEvent - Raw mouse event</li>
34265         * <li>dropNode - Dropped node(s).</li>
34266         * </ul>
34267         * @param {Object} dropEvent
34268         */
34269        "nodedrop" : true,
34270         /**
34271         * @event nodedragover
34272         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34273         * passed to handlers has the following properties:<br />
34274         * <ul style="padding:5px;padding-left:16px;">
34275         * <li>tree - The TreePanel</li>
34276         * <li>target - The node being targeted for the drop</li>
34277         * <li>data - The drag data from the drag source</li>
34278         * <li>point - The point of the drop - append, above or below</li>
34279         * <li>source - The drag source</li>
34280         * <li>rawEvent - Raw mouse event</li>
34281         * <li>dropNode - Drop node(s) provided by the source.</li>
34282         * <li>cancel - Set this to true to signal drop not allowed.</li>
34283         * </ul>
34284         * @param {Object} dragOverEvent
34285         */
34286        "nodedragover" : true,
34287        /**
34288         * @event appendnode
34289         * Fires when append node to the tree
34290         * @param {Roo.tree.TreePanel} this
34291         * @param {Roo.tree.TreeNode} node
34292         * @param {Number} index The index of the newly appended node
34293         */
34294        "appendnode" : true
34295         
34296     });
34297     if(this.singleExpand){
34298        this.on("beforeexpand", this.restrictExpand, this);
34299     }
34300     if (this.editor) {
34301         this.editor.tree = this;
34302         this.editor = Roo.factory(this.editor, Roo.tree);
34303     }
34304     
34305     if (this.selModel) {
34306         this.selModel = Roo.factory(this.selModel, Roo.tree);
34307     }
34308    
34309 };
34310 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34311     rootVisible : true,
34312     animate: Roo.enableFx,
34313     lines : true,
34314     enableDD : false,
34315     hlDrop : Roo.enableFx,
34316   
34317     renderer: false,
34318     
34319     rendererTip: false,
34320     // private
34321     restrictExpand : function(node){
34322         var p = node.parentNode;
34323         if(p){
34324             if(p.expandedChild && p.expandedChild.parentNode == p){
34325                 p.expandedChild.collapse();
34326             }
34327             p.expandedChild = node;
34328         }
34329     },
34330
34331     // private override
34332     setRootNode : function(node){
34333         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34334         if(!this.rootVisible){
34335             node.ui = new Roo.tree.RootTreeNodeUI(node);
34336         }
34337         return node;
34338     },
34339
34340     /**
34341      * Returns the container element for this TreePanel
34342      */
34343     getEl : function(){
34344         return this.el;
34345     },
34346
34347     /**
34348      * Returns the default TreeLoader for this TreePanel
34349      */
34350     getLoader : function(){
34351         return this.loader;
34352     },
34353
34354     /**
34355      * Expand all nodes
34356      */
34357     expandAll : function(){
34358         this.root.expand(true);
34359     },
34360
34361     /**
34362      * Collapse all nodes
34363      */
34364     collapseAll : function(){
34365         this.root.collapse(true);
34366     },
34367
34368     /**
34369      * Returns the selection model used by this TreePanel
34370      */
34371     getSelectionModel : function(){
34372         if(!this.selModel){
34373             this.selModel = new Roo.tree.DefaultSelectionModel();
34374         }
34375         return this.selModel;
34376     },
34377
34378     /**
34379      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34380      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34381      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34382      * @return {Array}
34383      */
34384     getChecked : function(a, startNode){
34385         startNode = startNode || this.root;
34386         var r = [];
34387         var f = function(){
34388             if(this.attributes.checked){
34389                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34390             }
34391         }
34392         startNode.cascade(f);
34393         return r;
34394     },
34395
34396     /**
34397      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34398      * @param {String} path
34399      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34400      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34401      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34402      */
34403     expandPath : function(path, attr, callback){
34404         attr = attr || "id";
34405         var keys = path.split(this.pathSeparator);
34406         var curNode = this.root;
34407         if(curNode.attributes[attr] != keys[1]){ // invalid root
34408             if(callback){
34409                 callback(false, null);
34410             }
34411             return;
34412         }
34413         var index = 1;
34414         var f = function(){
34415             if(++index == keys.length){
34416                 if(callback){
34417                     callback(true, curNode);
34418                 }
34419                 return;
34420             }
34421             var c = curNode.findChild(attr, keys[index]);
34422             if(!c){
34423                 if(callback){
34424                     callback(false, curNode);
34425                 }
34426                 return;
34427             }
34428             curNode = c;
34429             c.expand(false, false, f);
34430         };
34431         curNode.expand(false, false, f);
34432     },
34433
34434     /**
34435      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34436      * @param {String} path
34437      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34438      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34439      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34440      */
34441     selectPath : function(path, attr, callback){
34442         attr = attr || "id";
34443         var keys = path.split(this.pathSeparator);
34444         var v = keys.pop();
34445         if(keys.length > 0){
34446             var f = function(success, node){
34447                 if(success && node){
34448                     var n = node.findChild(attr, v);
34449                     if(n){
34450                         n.select();
34451                         if(callback){
34452                             callback(true, n);
34453                         }
34454                     }else if(callback){
34455                         callback(false, n);
34456                     }
34457                 }else{
34458                     if(callback){
34459                         callback(false, n);
34460                     }
34461                 }
34462             };
34463             this.expandPath(keys.join(this.pathSeparator), attr, f);
34464         }else{
34465             this.root.select();
34466             if(callback){
34467                 callback(true, this.root);
34468             }
34469         }
34470     },
34471
34472     getTreeEl : function(){
34473         return this.el;
34474     },
34475
34476     /**
34477      * Trigger rendering of this TreePanel
34478      */
34479     render : function(){
34480         if (this.innerCt) {
34481             return this; // stop it rendering more than once!!
34482         }
34483         
34484         this.innerCt = this.el.createChild({tag:"ul",
34485                cls:"x-tree-root-ct " +
34486                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34487
34488         if(this.containerScroll){
34489             Roo.dd.ScrollManager.register(this.el);
34490         }
34491         if((this.enableDD || this.enableDrop) && !this.dropZone){
34492            /**
34493             * The dropZone used by this tree if drop is enabled
34494             * @type Roo.tree.TreeDropZone
34495             */
34496              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34497                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34498            });
34499         }
34500         if((this.enableDD || this.enableDrag) && !this.dragZone){
34501            /**
34502             * The dragZone used by this tree if drag is enabled
34503             * @type Roo.tree.TreeDragZone
34504             */
34505             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34506                ddGroup: this.ddGroup || "TreeDD",
34507                scroll: this.ddScroll
34508            });
34509         }
34510         this.getSelectionModel().init(this);
34511         if (!this.root) {
34512             Roo.log("ROOT not set in tree");
34513             return this;
34514         }
34515         this.root.render();
34516         if(!this.rootVisible){
34517             this.root.renderChildren();
34518         }
34519         return this;
34520     }
34521 });/*
34522  * Based on:
34523  * Ext JS Library 1.1.1
34524  * Copyright(c) 2006-2007, Ext JS, LLC.
34525  *
34526  * Originally Released Under LGPL - original licence link has changed is not relivant.
34527  *
34528  * Fork - LGPL
34529  * <script type="text/javascript">
34530  */
34531  
34532
34533 /**
34534  * @class Roo.tree.DefaultSelectionModel
34535  * @extends Roo.util.Observable
34536  * The default single selection for a TreePanel.
34537  * @param {Object} cfg Configuration
34538  */
34539 Roo.tree.DefaultSelectionModel = function(cfg){
34540    this.selNode = null;
34541    
34542    
34543    
34544    this.addEvents({
34545        /**
34546         * @event selectionchange
34547         * Fires when the selected node changes
34548         * @param {DefaultSelectionModel} this
34549         * @param {TreeNode} node the new selection
34550         */
34551        "selectionchange" : true,
34552
34553        /**
34554         * @event beforeselect
34555         * Fires before the selected node changes, return false to cancel the change
34556         * @param {DefaultSelectionModel} this
34557         * @param {TreeNode} node the new selection
34558         * @param {TreeNode} node the old selection
34559         */
34560        "beforeselect" : true
34561    });
34562    
34563     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34564 };
34565
34566 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34567     init : function(tree){
34568         this.tree = tree;
34569         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34570         tree.on("click", this.onNodeClick, this);
34571     },
34572     
34573     onNodeClick : function(node, e){
34574         if (e.ctrlKey && this.selNode == node)  {
34575             this.unselect(node);
34576             return;
34577         }
34578         this.select(node);
34579     },
34580     
34581     /**
34582      * Select a node.
34583      * @param {TreeNode} node The node to select
34584      * @return {TreeNode} The selected node
34585      */
34586     select : function(node){
34587         var last = this.selNode;
34588         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34589             if(last){
34590                 last.ui.onSelectedChange(false);
34591             }
34592             this.selNode = node;
34593             node.ui.onSelectedChange(true);
34594             this.fireEvent("selectionchange", this, node, last);
34595         }
34596         return node;
34597     },
34598     
34599     /**
34600      * Deselect a node.
34601      * @param {TreeNode} node The node to unselect
34602      */
34603     unselect : function(node){
34604         if(this.selNode == node){
34605             this.clearSelections();
34606         }    
34607     },
34608     
34609     /**
34610      * Clear all selections
34611      */
34612     clearSelections : function(){
34613         var n = this.selNode;
34614         if(n){
34615             n.ui.onSelectedChange(false);
34616             this.selNode = null;
34617             this.fireEvent("selectionchange", this, null);
34618         }
34619         return n;
34620     },
34621     
34622     /**
34623      * Get the selected node
34624      * @return {TreeNode} The selected node
34625      */
34626     getSelectedNode : function(){
34627         return this.selNode;    
34628     },
34629     
34630     /**
34631      * Returns true if the node is selected
34632      * @param {TreeNode} node The node to check
34633      * @return {Boolean}
34634      */
34635     isSelected : function(node){
34636         return this.selNode == node;  
34637     },
34638
34639     /**
34640      * Selects the node above the selected node in the tree, intelligently walking the nodes
34641      * @return TreeNode The new selection
34642      */
34643     selectPrevious : function(){
34644         var s = this.selNode || this.lastSelNode;
34645         if(!s){
34646             return null;
34647         }
34648         var ps = s.previousSibling;
34649         if(ps){
34650             if(!ps.isExpanded() || ps.childNodes.length < 1){
34651                 return this.select(ps);
34652             } else{
34653                 var lc = ps.lastChild;
34654                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34655                     lc = lc.lastChild;
34656                 }
34657                 return this.select(lc);
34658             }
34659         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34660             return this.select(s.parentNode);
34661         }
34662         return null;
34663     },
34664
34665     /**
34666      * Selects the node above the selected node in the tree, intelligently walking the nodes
34667      * @return TreeNode The new selection
34668      */
34669     selectNext : function(){
34670         var s = this.selNode || this.lastSelNode;
34671         if(!s){
34672             return null;
34673         }
34674         if(s.firstChild && s.isExpanded()){
34675              return this.select(s.firstChild);
34676          }else if(s.nextSibling){
34677              return this.select(s.nextSibling);
34678          }else if(s.parentNode){
34679             var newS = null;
34680             s.parentNode.bubble(function(){
34681                 if(this.nextSibling){
34682                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34683                     return false;
34684                 }
34685             });
34686             return newS;
34687          }
34688         return null;
34689     },
34690
34691     onKeyDown : function(e){
34692         var s = this.selNode || this.lastSelNode;
34693         // undesirable, but required
34694         var sm = this;
34695         if(!s){
34696             return;
34697         }
34698         var k = e.getKey();
34699         switch(k){
34700              case e.DOWN:
34701                  e.stopEvent();
34702                  this.selectNext();
34703              break;
34704              case e.UP:
34705                  e.stopEvent();
34706                  this.selectPrevious();
34707              break;
34708              case e.RIGHT:
34709                  e.preventDefault();
34710                  if(s.hasChildNodes()){
34711                      if(!s.isExpanded()){
34712                          s.expand();
34713                      }else if(s.firstChild){
34714                          this.select(s.firstChild, e);
34715                      }
34716                  }
34717              break;
34718              case e.LEFT:
34719                  e.preventDefault();
34720                  if(s.hasChildNodes() && s.isExpanded()){
34721                      s.collapse();
34722                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34723                      this.select(s.parentNode, e);
34724                  }
34725              break;
34726         };
34727     }
34728 });
34729
34730 /**
34731  * @class Roo.tree.MultiSelectionModel
34732  * @extends Roo.util.Observable
34733  * Multi selection for a TreePanel.
34734  * @param {Object} cfg Configuration
34735  */
34736 Roo.tree.MultiSelectionModel = function(){
34737    this.selNodes = [];
34738    this.selMap = {};
34739    this.addEvents({
34740        /**
34741         * @event selectionchange
34742         * Fires when the selected nodes change
34743         * @param {MultiSelectionModel} this
34744         * @param {Array} nodes Array of the selected nodes
34745         */
34746        "selectionchange" : true
34747    });
34748    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34749    
34750 };
34751
34752 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34753     init : function(tree){
34754         this.tree = tree;
34755         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34756         tree.on("click", this.onNodeClick, this);
34757     },
34758     
34759     onNodeClick : function(node, e){
34760         this.select(node, e, e.ctrlKey);
34761     },
34762     
34763     /**
34764      * Select a node.
34765      * @param {TreeNode} node The node to select
34766      * @param {EventObject} e (optional) An event associated with the selection
34767      * @param {Boolean} keepExisting True to retain existing selections
34768      * @return {TreeNode} The selected node
34769      */
34770     select : function(node, e, keepExisting){
34771         if(keepExisting !== true){
34772             this.clearSelections(true);
34773         }
34774         if(this.isSelected(node)){
34775             this.lastSelNode = node;
34776             return node;
34777         }
34778         this.selNodes.push(node);
34779         this.selMap[node.id] = node;
34780         this.lastSelNode = node;
34781         node.ui.onSelectedChange(true);
34782         this.fireEvent("selectionchange", this, this.selNodes);
34783         return node;
34784     },
34785     
34786     /**
34787      * Deselect a node.
34788      * @param {TreeNode} node The node to unselect
34789      */
34790     unselect : function(node){
34791         if(this.selMap[node.id]){
34792             node.ui.onSelectedChange(false);
34793             var sn = this.selNodes;
34794             var index = -1;
34795             if(sn.indexOf){
34796                 index = sn.indexOf(node);
34797             }else{
34798                 for(var i = 0, len = sn.length; i < len; i++){
34799                     if(sn[i] == node){
34800                         index = i;
34801                         break;
34802                     }
34803                 }
34804             }
34805             if(index != -1){
34806                 this.selNodes.splice(index, 1);
34807             }
34808             delete this.selMap[node.id];
34809             this.fireEvent("selectionchange", this, this.selNodes);
34810         }
34811     },
34812     
34813     /**
34814      * Clear all selections
34815      */
34816     clearSelections : function(suppressEvent){
34817         var sn = this.selNodes;
34818         if(sn.length > 0){
34819             for(var i = 0, len = sn.length; i < len; i++){
34820                 sn[i].ui.onSelectedChange(false);
34821             }
34822             this.selNodes = [];
34823             this.selMap = {};
34824             if(suppressEvent !== true){
34825                 this.fireEvent("selectionchange", this, this.selNodes);
34826             }
34827         }
34828     },
34829     
34830     /**
34831      * Returns true if the node is selected
34832      * @param {TreeNode} node The node to check
34833      * @return {Boolean}
34834      */
34835     isSelected : function(node){
34836         return this.selMap[node.id] ? true : false;  
34837     },
34838     
34839     /**
34840      * Returns an array of the selected nodes
34841      * @return {Array}
34842      */
34843     getSelectedNodes : function(){
34844         return this.selNodes;    
34845     },
34846
34847     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34848
34849     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34850
34851     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34852 });/*
34853  * Based on:
34854  * Ext JS Library 1.1.1
34855  * Copyright(c) 2006-2007, Ext JS, LLC.
34856  *
34857  * Originally Released Under LGPL - original licence link has changed is not relivant.
34858  *
34859  * Fork - LGPL
34860  * <script type="text/javascript">
34861  */
34862  
34863 /**
34864  * @class Roo.tree.TreeNode
34865  * @extends Roo.data.Node
34866  * @cfg {String} text The text for this node
34867  * @cfg {Boolean} expanded true to start the node expanded
34868  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34869  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34870  * @cfg {Boolean} disabled true to start the node disabled
34871  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34872  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34873  * @cfg {String} cls A css class to be added to the node
34874  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34875  * @cfg {String} href URL of the link used for the node (defaults to #)
34876  * @cfg {String} hrefTarget target frame for the link
34877  * @cfg {String} qtip An Ext QuickTip for the node
34878  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34879  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34880  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34881  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34882  * (defaults to undefined with no checkbox rendered)
34883  * @constructor
34884  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34885  */
34886 Roo.tree.TreeNode = function(attributes){
34887     attributes = attributes || {};
34888     if(typeof attributes == "string"){
34889         attributes = {text: attributes};
34890     }
34891     this.childrenRendered = false;
34892     this.rendered = false;
34893     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34894     this.expanded = attributes.expanded === true;
34895     this.isTarget = attributes.isTarget !== false;
34896     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34897     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34898
34899     /**
34900      * Read-only. The text for this node. To change it use setText().
34901      * @type String
34902      */
34903     this.text = attributes.text;
34904     /**
34905      * True if this node is disabled.
34906      * @type Boolean
34907      */
34908     this.disabled = attributes.disabled === true;
34909
34910     this.addEvents({
34911         /**
34912         * @event textchange
34913         * Fires when the text for this node is changed
34914         * @param {Node} this This node
34915         * @param {String} text The new text
34916         * @param {String} oldText The old text
34917         */
34918         "textchange" : true,
34919         /**
34920         * @event beforeexpand
34921         * Fires before this node is expanded, return false to cancel.
34922         * @param {Node} this This node
34923         * @param {Boolean} deep
34924         * @param {Boolean} anim
34925         */
34926         "beforeexpand" : true,
34927         /**
34928         * @event beforecollapse
34929         * Fires before this node is collapsed, return false to cancel.
34930         * @param {Node} this This node
34931         * @param {Boolean} deep
34932         * @param {Boolean} anim
34933         */
34934         "beforecollapse" : true,
34935         /**
34936         * @event expand
34937         * Fires when this node is expanded
34938         * @param {Node} this This node
34939         */
34940         "expand" : true,
34941         /**
34942         * @event disabledchange
34943         * Fires when the disabled status of this node changes
34944         * @param {Node} this This node
34945         * @param {Boolean} disabled
34946         */
34947         "disabledchange" : true,
34948         /**
34949         * @event collapse
34950         * Fires when this node is collapsed
34951         * @param {Node} this This node
34952         */
34953         "collapse" : true,
34954         /**
34955         * @event beforeclick
34956         * Fires before click processing. Return false to cancel the default action.
34957         * @param {Node} this This node
34958         * @param {Roo.EventObject} e The event object
34959         */
34960         "beforeclick":true,
34961         /**
34962         * @event checkchange
34963         * Fires when a node with a checkbox's checked property changes
34964         * @param {Node} this This node
34965         * @param {Boolean} checked
34966         */
34967         "checkchange":true,
34968         /**
34969         * @event click
34970         * Fires when this node is clicked
34971         * @param {Node} this This node
34972         * @param {Roo.EventObject} e The event object
34973         */
34974         "click":true,
34975         /**
34976         * @event dblclick
34977         * Fires when this node is double clicked
34978         * @param {Node} this This node
34979         * @param {Roo.EventObject} e The event object
34980         */
34981         "dblclick":true,
34982         /**
34983         * @event contextmenu
34984         * Fires when this node is right clicked
34985         * @param {Node} this This node
34986         * @param {Roo.EventObject} e The event object
34987         */
34988         "contextmenu":true,
34989         /**
34990         * @event beforechildrenrendered
34991         * Fires right before the child nodes for this node are rendered
34992         * @param {Node} this This node
34993         */
34994         "beforechildrenrendered":true
34995     });
34996
34997     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34998
34999     /**
35000      * Read-only. The UI for this node
35001      * @type TreeNodeUI
35002      */
35003     this.ui = new uiClass(this);
35004     
35005     // finally support items[]
35006     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35007         return;
35008     }
35009     
35010     
35011     Roo.each(this.attributes.items, function(c) {
35012         this.appendChild(Roo.factory(c,Roo.Tree));
35013     }, this);
35014     delete this.attributes.items;
35015     
35016     
35017     
35018 };
35019 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35020     preventHScroll: true,
35021     /**
35022      * Returns true if this node is expanded
35023      * @return {Boolean}
35024      */
35025     isExpanded : function(){
35026         return this.expanded;
35027     },
35028
35029     /**
35030      * Returns the UI object for this node
35031      * @return {TreeNodeUI}
35032      */
35033     getUI : function(){
35034         return this.ui;
35035     },
35036
35037     // private override
35038     setFirstChild : function(node){
35039         var of = this.firstChild;
35040         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35041         if(this.childrenRendered && of && node != of){
35042             of.renderIndent(true, true);
35043         }
35044         if(this.rendered){
35045             this.renderIndent(true, true);
35046         }
35047     },
35048
35049     // private override
35050     setLastChild : function(node){
35051         var ol = this.lastChild;
35052         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35053         if(this.childrenRendered && ol && node != ol){
35054             ol.renderIndent(true, true);
35055         }
35056         if(this.rendered){
35057             this.renderIndent(true, true);
35058         }
35059     },
35060
35061     // these methods are overridden to provide lazy rendering support
35062     // private override
35063     appendChild : function()
35064     {
35065         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35066         if(node && this.childrenRendered){
35067             node.render();
35068         }
35069         this.ui.updateExpandIcon();
35070         return node;
35071     },
35072
35073     // private override
35074     removeChild : function(node){
35075         this.ownerTree.getSelectionModel().unselect(node);
35076         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35077         // if it's been rendered remove dom node
35078         if(this.childrenRendered){
35079             node.ui.remove();
35080         }
35081         if(this.childNodes.length < 1){
35082             this.collapse(false, false);
35083         }else{
35084             this.ui.updateExpandIcon();
35085         }
35086         if(!this.firstChild) {
35087             this.childrenRendered = false;
35088         }
35089         return node;
35090     },
35091
35092     // private override
35093     insertBefore : function(node, refNode){
35094         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35095         if(newNode && refNode && this.childrenRendered){
35096             node.render();
35097         }
35098         this.ui.updateExpandIcon();
35099         return newNode;
35100     },
35101
35102     /**
35103      * Sets the text for this node
35104      * @param {String} text
35105      */
35106     setText : function(text){
35107         var oldText = this.text;
35108         this.text = text;
35109         this.attributes.text = text;
35110         if(this.rendered){ // event without subscribing
35111             this.ui.onTextChange(this, text, oldText);
35112         }
35113         this.fireEvent("textchange", this, text, oldText);
35114     },
35115
35116     /**
35117      * Triggers selection of this node
35118      */
35119     select : function(){
35120         this.getOwnerTree().getSelectionModel().select(this);
35121     },
35122
35123     /**
35124      * Triggers deselection of this node
35125      */
35126     unselect : function(){
35127         this.getOwnerTree().getSelectionModel().unselect(this);
35128     },
35129
35130     /**
35131      * Returns true if this node is selected
35132      * @return {Boolean}
35133      */
35134     isSelected : function(){
35135         return this.getOwnerTree().getSelectionModel().isSelected(this);
35136     },
35137
35138     /**
35139      * Expand this node.
35140      * @param {Boolean} deep (optional) True to expand all children as well
35141      * @param {Boolean} anim (optional) false to cancel the default animation
35142      * @param {Function} callback (optional) A callback to be called when
35143      * expanding this node completes (does not wait for deep expand to complete).
35144      * Called with 1 parameter, this node.
35145      */
35146     expand : function(deep, anim, callback){
35147         if(!this.expanded){
35148             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35149                 return;
35150             }
35151             if(!this.childrenRendered){
35152                 this.renderChildren();
35153             }
35154             this.expanded = true;
35155             
35156             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35157                 this.ui.animExpand(function(){
35158                     this.fireEvent("expand", this);
35159                     if(typeof callback == "function"){
35160                         callback(this);
35161                     }
35162                     if(deep === true){
35163                         this.expandChildNodes(true);
35164                     }
35165                 }.createDelegate(this));
35166                 return;
35167             }else{
35168                 this.ui.expand();
35169                 this.fireEvent("expand", this);
35170                 if(typeof callback == "function"){
35171                     callback(this);
35172                 }
35173             }
35174         }else{
35175            if(typeof callback == "function"){
35176                callback(this);
35177            }
35178         }
35179         if(deep === true){
35180             this.expandChildNodes(true);
35181         }
35182     },
35183
35184     isHiddenRoot : function(){
35185         return this.isRoot && !this.getOwnerTree().rootVisible;
35186     },
35187
35188     /**
35189      * Collapse this node.
35190      * @param {Boolean} deep (optional) True to collapse all children as well
35191      * @param {Boolean} anim (optional) false to cancel the default animation
35192      */
35193     collapse : function(deep, anim){
35194         if(this.expanded && !this.isHiddenRoot()){
35195             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35196                 return;
35197             }
35198             this.expanded = false;
35199             if((this.getOwnerTree().animate && anim !== false) || anim){
35200                 this.ui.animCollapse(function(){
35201                     this.fireEvent("collapse", this);
35202                     if(deep === true){
35203                         this.collapseChildNodes(true);
35204                     }
35205                 }.createDelegate(this));
35206                 return;
35207             }else{
35208                 this.ui.collapse();
35209                 this.fireEvent("collapse", this);
35210             }
35211         }
35212         if(deep === true){
35213             var cs = this.childNodes;
35214             for(var i = 0, len = cs.length; i < len; i++) {
35215                 cs[i].collapse(true, false);
35216             }
35217         }
35218     },
35219
35220     // private
35221     delayedExpand : function(delay){
35222         if(!this.expandProcId){
35223             this.expandProcId = this.expand.defer(delay, this);
35224         }
35225     },
35226
35227     // private
35228     cancelExpand : function(){
35229         if(this.expandProcId){
35230             clearTimeout(this.expandProcId);
35231         }
35232         this.expandProcId = false;
35233     },
35234
35235     /**
35236      * Toggles expanded/collapsed state of the node
35237      */
35238     toggle : function(){
35239         if(this.expanded){
35240             this.collapse();
35241         }else{
35242             this.expand();
35243         }
35244     },
35245
35246     /**
35247      * Ensures all parent nodes are expanded
35248      */
35249     ensureVisible : function(callback){
35250         var tree = this.getOwnerTree();
35251         tree.expandPath(this.parentNode.getPath(), false, function(){
35252             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35253             Roo.callback(callback);
35254         }.createDelegate(this));
35255     },
35256
35257     /**
35258      * Expand all child nodes
35259      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35260      */
35261     expandChildNodes : function(deep){
35262         var cs = this.childNodes;
35263         for(var i = 0, len = cs.length; i < len; i++) {
35264                 cs[i].expand(deep);
35265         }
35266     },
35267
35268     /**
35269      * Collapse all child nodes
35270      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35271      */
35272     collapseChildNodes : function(deep){
35273         var cs = this.childNodes;
35274         for(var i = 0, len = cs.length; i < len; i++) {
35275                 cs[i].collapse(deep);
35276         }
35277     },
35278
35279     /**
35280      * Disables this node
35281      */
35282     disable : function(){
35283         this.disabled = true;
35284         this.unselect();
35285         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35286             this.ui.onDisableChange(this, true);
35287         }
35288         this.fireEvent("disabledchange", this, true);
35289     },
35290
35291     /**
35292      * Enables this node
35293      */
35294     enable : function(){
35295         this.disabled = false;
35296         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35297             this.ui.onDisableChange(this, false);
35298         }
35299         this.fireEvent("disabledchange", this, false);
35300     },
35301
35302     // private
35303     renderChildren : function(suppressEvent){
35304         if(suppressEvent !== false){
35305             this.fireEvent("beforechildrenrendered", this);
35306         }
35307         var cs = this.childNodes;
35308         for(var i = 0, len = cs.length; i < len; i++){
35309             cs[i].render(true);
35310         }
35311         this.childrenRendered = true;
35312     },
35313
35314     // private
35315     sort : function(fn, scope){
35316         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35317         if(this.childrenRendered){
35318             var cs = this.childNodes;
35319             for(var i = 0, len = cs.length; i < len; i++){
35320                 cs[i].render(true);
35321             }
35322         }
35323     },
35324
35325     // private
35326     render : function(bulkRender){
35327         this.ui.render(bulkRender);
35328         if(!this.rendered){
35329             this.rendered = true;
35330             if(this.expanded){
35331                 this.expanded = false;
35332                 this.expand(false, false);
35333             }
35334         }
35335     },
35336
35337     // private
35338     renderIndent : function(deep, refresh){
35339         if(refresh){
35340             this.ui.childIndent = null;
35341         }
35342         this.ui.renderIndent();
35343         if(deep === true && this.childrenRendered){
35344             var cs = this.childNodes;
35345             for(var i = 0, len = cs.length; i < len; i++){
35346                 cs[i].renderIndent(true, refresh);
35347             }
35348         }
35349     }
35350 });/*
35351  * Based on:
35352  * Ext JS Library 1.1.1
35353  * Copyright(c) 2006-2007, Ext JS, LLC.
35354  *
35355  * Originally Released Under LGPL - original licence link has changed is not relivant.
35356  *
35357  * Fork - LGPL
35358  * <script type="text/javascript">
35359  */
35360  
35361 /**
35362  * @class Roo.tree.AsyncTreeNode
35363  * @extends Roo.tree.TreeNode
35364  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35365  * @constructor
35366  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35367  */
35368  Roo.tree.AsyncTreeNode = function(config){
35369     this.loaded = false;
35370     this.loading = false;
35371     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35372     /**
35373     * @event beforeload
35374     * Fires before this node is loaded, return false to cancel
35375     * @param {Node} this This node
35376     */
35377     this.addEvents({'beforeload':true, 'load': true});
35378     /**
35379     * @event load
35380     * Fires when this node is loaded
35381     * @param {Node} this This node
35382     */
35383     /**
35384      * The loader used by this node (defaults to using the tree's defined loader)
35385      * @type TreeLoader
35386      * @property loader
35387      */
35388 };
35389 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35390     expand : function(deep, anim, callback){
35391         if(this.loading){ // if an async load is already running, waiting til it's done
35392             var timer;
35393             var f = function(){
35394                 if(!this.loading){ // done loading
35395                     clearInterval(timer);
35396                     this.expand(deep, anim, callback);
35397                 }
35398             }.createDelegate(this);
35399             timer = setInterval(f, 200);
35400             return;
35401         }
35402         if(!this.loaded){
35403             if(this.fireEvent("beforeload", this) === false){
35404                 return;
35405             }
35406             this.loading = true;
35407             this.ui.beforeLoad(this);
35408             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35409             if(loader){
35410                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35411                 return;
35412             }
35413         }
35414         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35415     },
35416     
35417     /**
35418      * Returns true if this node is currently loading
35419      * @return {Boolean}
35420      */
35421     isLoading : function(){
35422         return this.loading;  
35423     },
35424     
35425     loadComplete : function(deep, anim, callback){
35426         this.loading = false;
35427         this.loaded = true;
35428         this.ui.afterLoad(this);
35429         this.fireEvent("load", this);
35430         this.expand(deep, anim, callback);
35431     },
35432     
35433     /**
35434      * Returns true if this node has been loaded
35435      * @return {Boolean}
35436      */
35437     isLoaded : function(){
35438         return this.loaded;
35439     },
35440     
35441     hasChildNodes : function(){
35442         if(!this.isLeaf() && !this.loaded){
35443             return true;
35444         }else{
35445             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35446         }
35447     },
35448
35449     /**
35450      * Trigger a reload for this node
35451      * @param {Function} callback
35452      */
35453     reload : function(callback){
35454         this.collapse(false, false);
35455         while(this.firstChild){
35456             this.removeChild(this.firstChild);
35457         }
35458         this.childrenRendered = false;
35459         this.loaded = false;
35460         if(this.isHiddenRoot()){
35461             this.expanded = false;
35462         }
35463         this.expand(false, false, callback);
35464     }
35465 });/*
35466  * Based on:
35467  * Ext JS Library 1.1.1
35468  * Copyright(c) 2006-2007, Ext JS, LLC.
35469  *
35470  * Originally Released Under LGPL - original licence link has changed is not relivant.
35471  *
35472  * Fork - LGPL
35473  * <script type="text/javascript">
35474  */
35475  
35476 /**
35477  * @class Roo.tree.TreeNodeUI
35478  * @constructor
35479  * @param {Object} node The node to render
35480  * The TreeNode UI implementation is separate from the
35481  * tree implementation. Unless you are customizing the tree UI,
35482  * you should never have to use this directly.
35483  */
35484 Roo.tree.TreeNodeUI = function(node){
35485     this.node = node;
35486     this.rendered = false;
35487     this.animating = false;
35488     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35489 };
35490
35491 Roo.tree.TreeNodeUI.prototype = {
35492     removeChild : function(node){
35493         if(this.rendered){
35494             this.ctNode.removeChild(node.ui.getEl());
35495         }
35496     },
35497
35498     beforeLoad : function(){
35499          this.addClass("x-tree-node-loading");
35500     },
35501
35502     afterLoad : function(){
35503          this.removeClass("x-tree-node-loading");
35504     },
35505
35506     onTextChange : function(node, text, oldText){
35507         if(this.rendered){
35508             this.textNode.innerHTML = text;
35509         }
35510     },
35511
35512     onDisableChange : function(node, state){
35513         this.disabled = state;
35514         if(state){
35515             this.addClass("x-tree-node-disabled");
35516         }else{
35517             this.removeClass("x-tree-node-disabled");
35518         }
35519     },
35520
35521     onSelectedChange : function(state){
35522         if(state){
35523             this.focus();
35524             this.addClass("x-tree-selected");
35525         }else{
35526             //this.blur();
35527             this.removeClass("x-tree-selected");
35528         }
35529     },
35530
35531     onMove : function(tree, node, oldParent, newParent, index, refNode){
35532         this.childIndent = null;
35533         if(this.rendered){
35534             var targetNode = newParent.ui.getContainer();
35535             if(!targetNode){//target not rendered
35536                 this.holder = document.createElement("div");
35537                 this.holder.appendChild(this.wrap);
35538                 return;
35539             }
35540             var insertBefore = refNode ? refNode.ui.getEl() : null;
35541             if(insertBefore){
35542                 targetNode.insertBefore(this.wrap, insertBefore);
35543             }else{
35544                 targetNode.appendChild(this.wrap);
35545             }
35546             this.node.renderIndent(true);
35547         }
35548     },
35549
35550     addClass : function(cls){
35551         if(this.elNode){
35552             Roo.fly(this.elNode).addClass(cls);
35553         }
35554     },
35555
35556     removeClass : function(cls){
35557         if(this.elNode){
35558             Roo.fly(this.elNode).removeClass(cls);
35559         }
35560     },
35561
35562     remove : function(){
35563         if(this.rendered){
35564             this.holder = document.createElement("div");
35565             this.holder.appendChild(this.wrap);
35566         }
35567     },
35568
35569     fireEvent : function(){
35570         return this.node.fireEvent.apply(this.node, arguments);
35571     },
35572
35573     initEvents : function(){
35574         this.node.on("move", this.onMove, this);
35575         var E = Roo.EventManager;
35576         var a = this.anchor;
35577
35578         var el = Roo.fly(a, '_treeui');
35579
35580         if(Roo.isOpera){ // opera render bug ignores the CSS
35581             el.setStyle("text-decoration", "none");
35582         }
35583
35584         el.on("click", this.onClick, this);
35585         el.on("dblclick", this.onDblClick, this);
35586
35587         if(this.checkbox){
35588             Roo.EventManager.on(this.checkbox,
35589                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35590         }
35591
35592         el.on("contextmenu", this.onContextMenu, this);
35593
35594         var icon = Roo.fly(this.iconNode);
35595         icon.on("click", this.onClick, this);
35596         icon.on("dblclick", this.onDblClick, this);
35597         icon.on("contextmenu", this.onContextMenu, this);
35598         E.on(this.ecNode, "click", this.ecClick, this, true);
35599
35600         if(this.node.disabled){
35601             this.addClass("x-tree-node-disabled");
35602         }
35603         if(this.node.hidden){
35604             this.addClass("x-tree-node-disabled");
35605         }
35606         var ot = this.node.getOwnerTree();
35607         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35608         if(dd && (!this.node.isRoot || ot.rootVisible)){
35609             Roo.dd.Registry.register(this.elNode, {
35610                 node: this.node,
35611                 handles: this.getDDHandles(),
35612                 isHandle: false
35613             });
35614         }
35615     },
35616
35617     getDDHandles : function(){
35618         return [this.iconNode, this.textNode];
35619     },
35620
35621     hide : function(){
35622         if(this.rendered){
35623             this.wrap.style.display = "none";
35624         }
35625     },
35626
35627     show : function(){
35628         if(this.rendered){
35629             this.wrap.style.display = "";
35630         }
35631     },
35632
35633     onContextMenu : function(e){
35634         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35635             e.preventDefault();
35636             this.focus();
35637             this.fireEvent("contextmenu", this.node, e);
35638         }
35639     },
35640
35641     onClick : function(e){
35642         if(this.dropping){
35643             e.stopEvent();
35644             return;
35645         }
35646         if(this.fireEvent("beforeclick", this.node, e) !== false){
35647             if(!this.disabled && this.node.attributes.href){
35648                 this.fireEvent("click", this.node, e);
35649                 return;
35650             }
35651             e.preventDefault();
35652             if(this.disabled){
35653                 return;
35654             }
35655
35656             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35657                 this.node.toggle();
35658             }
35659
35660             this.fireEvent("click", this.node, e);
35661         }else{
35662             e.stopEvent();
35663         }
35664     },
35665
35666     onDblClick : function(e){
35667         e.preventDefault();
35668         if(this.disabled){
35669             return;
35670         }
35671         if(this.checkbox){
35672             this.toggleCheck();
35673         }
35674         if(!this.animating && this.node.hasChildNodes()){
35675             this.node.toggle();
35676         }
35677         this.fireEvent("dblclick", this.node, e);
35678     },
35679
35680     onCheckChange : function(){
35681         var checked = this.checkbox.checked;
35682         this.node.attributes.checked = checked;
35683         this.fireEvent('checkchange', this.node, checked);
35684     },
35685
35686     ecClick : function(e){
35687         if(!this.animating && this.node.hasChildNodes()){
35688             this.node.toggle();
35689         }
35690     },
35691
35692     startDrop : function(){
35693         this.dropping = true;
35694     },
35695
35696     // delayed drop so the click event doesn't get fired on a drop
35697     endDrop : function(){
35698        setTimeout(function(){
35699            this.dropping = false;
35700        }.createDelegate(this), 50);
35701     },
35702
35703     expand : function(){
35704         this.updateExpandIcon();
35705         this.ctNode.style.display = "";
35706     },
35707
35708     focus : function(){
35709         if(!this.node.preventHScroll){
35710             try{this.anchor.focus();
35711             }catch(e){}
35712         }else if(!Roo.isIE){
35713             try{
35714                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35715                 var l = noscroll.scrollLeft;
35716                 this.anchor.focus();
35717                 noscroll.scrollLeft = l;
35718             }catch(e){}
35719         }
35720     },
35721
35722     toggleCheck : function(value){
35723         var cb = this.checkbox;
35724         if(cb){
35725             cb.checked = (value === undefined ? !cb.checked : value);
35726         }
35727     },
35728
35729     blur : function(){
35730         try{
35731             this.anchor.blur();
35732         }catch(e){}
35733     },
35734
35735     animExpand : function(callback){
35736         var ct = Roo.get(this.ctNode);
35737         ct.stopFx();
35738         if(!this.node.hasChildNodes()){
35739             this.updateExpandIcon();
35740             this.ctNode.style.display = "";
35741             Roo.callback(callback);
35742             return;
35743         }
35744         this.animating = true;
35745         this.updateExpandIcon();
35746
35747         ct.slideIn('t', {
35748            callback : function(){
35749                this.animating = false;
35750                Roo.callback(callback);
35751             },
35752             scope: this,
35753             duration: this.node.ownerTree.duration || .25
35754         });
35755     },
35756
35757     highlight : function(){
35758         var tree = this.node.getOwnerTree();
35759         Roo.fly(this.wrap).highlight(
35760             tree.hlColor || "C3DAF9",
35761             {endColor: tree.hlBaseColor}
35762         );
35763     },
35764
35765     collapse : function(){
35766         this.updateExpandIcon();
35767         this.ctNode.style.display = "none";
35768     },
35769
35770     animCollapse : function(callback){
35771         var ct = Roo.get(this.ctNode);
35772         ct.enableDisplayMode('block');
35773         ct.stopFx();
35774
35775         this.animating = true;
35776         this.updateExpandIcon();
35777
35778         ct.slideOut('t', {
35779             callback : function(){
35780                this.animating = false;
35781                Roo.callback(callback);
35782             },
35783             scope: this,
35784             duration: this.node.ownerTree.duration || .25
35785         });
35786     },
35787
35788     getContainer : function(){
35789         return this.ctNode;
35790     },
35791
35792     getEl : function(){
35793         return this.wrap;
35794     },
35795
35796     appendDDGhost : function(ghostNode){
35797         ghostNode.appendChild(this.elNode.cloneNode(true));
35798     },
35799
35800     getDDRepairXY : function(){
35801         return Roo.lib.Dom.getXY(this.iconNode);
35802     },
35803
35804     onRender : function(){
35805         this.render();
35806     },
35807
35808     render : function(bulkRender){
35809         var n = this.node, a = n.attributes;
35810         var targetNode = n.parentNode ?
35811               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35812
35813         if(!this.rendered){
35814             this.rendered = true;
35815
35816             this.renderElements(n, a, targetNode, bulkRender);
35817
35818             if(a.qtip){
35819                if(this.textNode.setAttributeNS){
35820                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35821                    if(a.qtipTitle){
35822                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35823                    }
35824                }else{
35825                    this.textNode.setAttribute("ext:qtip", a.qtip);
35826                    if(a.qtipTitle){
35827                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35828                    }
35829                }
35830             }else if(a.qtipCfg){
35831                 a.qtipCfg.target = Roo.id(this.textNode);
35832                 Roo.QuickTips.register(a.qtipCfg);
35833             }
35834             this.initEvents();
35835             if(!this.node.expanded){
35836                 this.updateExpandIcon();
35837             }
35838         }else{
35839             if(bulkRender === true) {
35840                 targetNode.appendChild(this.wrap);
35841             }
35842         }
35843     },
35844
35845     renderElements : function(n, a, targetNode, bulkRender)
35846     {
35847         // add some indent caching, this helps performance when rendering a large tree
35848         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35849         var t = n.getOwnerTree();
35850         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35851         if (typeof(n.attributes.html) != 'undefined') {
35852             txt = n.attributes.html;
35853         }
35854         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35855         var cb = typeof a.checked == 'boolean';
35856         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35857         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35858             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35859             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35860             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35861             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35862             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35863              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35864                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35865             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35866             "</li>"];
35867
35868         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35869             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35870                                 n.nextSibling.ui.getEl(), buf.join(""));
35871         }else{
35872             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35873         }
35874
35875         this.elNode = this.wrap.childNodes[0];
35876         this.ctNode = this.wrap.childNodes[1];
35877         var cs = this.elNode.childNodes;
35878         this.indentNode = cs[0];
35879         this.ecNode = cs[1];
35880         this.iconNode = cs[2];
35881         var index = 3;
35882         if(cb){
35883             this.checkbox = cs[3];
35884             index++;
35885         }
35886         this.anchor = cs[index];
35887         this.textNode = cs[index].firstChild;
35888     },
35889
35890     getAnchor : function(){
35891         return this.anchor;
35892     },
35893
35894     getTextEl : function(){
35895         return this.textNode;
35896     },
35897
35898     getIconEl : function(){
35899         return this.iconNode;
35900     },
35901
35902     isChecked : function(){
35903         return this.checkbox ? this.checkbox.checked : false;
35904     },
35905
35906     updateExpandIcon : function(){
35907         if(this.rendered){
35908             var n = this.node, c1, c2;
35909             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35910             var hasChild = n.hasChildNodes();
35911             if(hasChild){
35912                 if(n.expanded){
35913                     cls += "-minus";
35914                     c1 = "x-tree-node-collapsed";
35915                     c2 = "x-tree-node-expanded";
35916                 }else{
35917                     cls += "-plus";
35918                     c1 = "x-tree-node-expanded";
35919                     c2 = "x-tree-node-collapsed";
35920                 }
35921                 if(this.wasLeaf){
35922                     this.removeClass("x-tree-node-leaf");
35923                     this.wasLeaf = false;
35924                 }
35925                 if(this.c1 != c1 || this.c2 != c2){
35926                     Roo.fly(this.elNode).replaceClass(c1, c2);
35927                     this.c1 = c1; this.c2 = c2;
35928                 }
35929             }else{
35930                 // this changes non-leafs into leafs if they have no children.
35931                 // it's not very rational behaviour..
35932                 
35933                 if(!this.wasLeaf && this.node.leaf){
35934                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35935                     delete this.c1;
35936                     delete this.c2;
35937                     this.wasLeaf = true;
35938                 }
35939             }
35940             var ecc = "x-tree-ec-icon "+cls;
35941             if(this.ecc != ecc){
35942                 this.ecNode.className = ecc;
35943                 this.ecc = ecc;
35944             }
35945         }
35946     },
35947
35948     getChildIndent : function(){
35949         if(!this.childIndent){
35950             var buf = [];
35951             var p = this.node;
35952             while(p){
35953                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35954                     if(!p.isLast()) {
35955                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35956                     } else {
35957                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35958                     }
35959                 }
35960                 p = p.parentNode;
35961             }
35962             this.childIndent = buf.join("");
35963         }
35964         return this.childIndent;
35965     },
35966
35967     renderIndent : function(){
35968         if(this.rendered){
35969             var indent = "";
35970             var p = this.node.parentNode;
35971             if(p){
35972                 indent = p.ui.getChildIndent();
35973             }
35974             if(this.indentMarkup != indent){ // don't rerender if not required
35975                 this.indentNode.innerHTML = indent;
35976                 this.indentMarkup = indent;
35977             }
35978             this.updateExpandIcon();
35979         }
35980     }
35981 };
35982
35983 Roo.tree.RootTreeNodeUI = function(){
35984     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35985 };
35986 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35987     render : function(){
35988         if(!this.rendered){
35989             var targetNode = this.node.ownerTree.innerCt.dom;
35990             this.node.expanded = true;
35991             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35992             this.wrap = this.ctNode = targetNode.firstChild;
35993         }
35994     },
35995     collapse : function(){
35996     },
35997     expand : function(){
35998     }
35999 });/*
36000  * Based on:
36001  * Ext JS Library 1.1.1
36002  * Copyright(c) 2006-2007, Ext JS, LLC.
36003  *
36004  * Originally Released Under LGPL - original licence link has changed is not relivant.
36005  *
36006  * Fork - LGPL
36007  * <script type="text/javascript">
36008  */
36009 /**
36010  * @class Roo.tree.TreeLoader
36011  * @extends Roo.util.Observable
36012  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36013  * nodes from a specified URL. The response must be a javascript Array definition
36014  * who's elements are node definition objects. eg:
36015  * <pre><code>
36016 {  success : true,
36017    data :      [
36018    
36019     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36020     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36021     ]
36022 }
36023
36024
36025 </code></pre>
36026  * <br><br>
36027  * The old style respose with just an array is still supported, but not recommended.
36028  * <br><br>
36029  *
36030  * A server request is sent, and child nodes are loaded only when a node is expanded.
36031  * The loading node's id is passed to the server under the parameter name "node" to
36032  * enable the server to produce the correct child nodes.
36033  * <br><br>
36034  * To pass extra parameters, an event handler may be attached to the "beforeload"
36035  * event, and the parameters specified in the TreeLoader's baseParams property:
36036  * <pre><code>
36037     myTreeLoader.on("beforeload", function(treeLoader, node) {
36038         this.baseParams.category = node.attributes.category;
36039     }, this);
36040     
36041 </code></pre>
36042  *
36043  * This would pass an HTTP parameter called "category" to the server containing
36044  * the value of the Node's "category" attribute.
36045  * @constructor
36046  * Creates a new Treeloader.
36047  * @param {Object} config A config object containing config properties.
36048  */
36049 Roo.tree.TreeLoader = function(config){
36050     this.baseParams = {};
36051     this.requestMethod = "POST";
36052     Roo.apply(this, config);
36053
36054     this.addEvents({
36055     
36056         /**
36057          * @event beforeload
36058          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36059          * @param {Object} This TreeLoader object.
36060          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36061          * @param {Object} callback The callback function specified in the {@link #load} call.
36062          */
36063         beforeload : true,
36064         /**
36065          * @event load
36066          * Fires when the node has been successfuly loaded.
36067          * @param {Object} This TreeLoader object.
36068          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36069          * @param {Object} response The response object containing the data from the server.
36070          */
36071         load : true,
36072         /**
36073          * @event loadexception
36074          * Fires if the network request failed.
36075          * @param {Object} This TreeLoader object.
36076          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36077          * @param {Object} response The response object containing the data from the server.
36078          */
36079         loadexception : true,
36080         /**
36081          * @event create
36082          * Fires before a node is created, enabling you to return custom Node types 
36083          * @param {Object} This TreeLoader object.
36084          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36085          */
36086         create : true
36087     });
36088
36089     Roo.tree.TreeLoader.superclass.constructor.call(this);
36090 };
36091
36092 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36093     /**
36094     * @cfg {String} dataUrl The URL from which to request a Json string which
36095     * specifies an array of node definition object representing the child nodes
36096     * to be loaded.
36097     */
36098     /**
36099     * @cfg {String} requestMethod either GET or POST
36100     * defaults to POST (due to BC)
36101     * to be loaded.
36102     */
36103     /**
36104     * @cfg {Object} baseParams (optional) An object containing properties which
36105     * specify HTTP parameters to be passed to each request for child nodes.
36106     */
36107     /**
36108     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36109     * created by this loader. If the attributes sent by the server have an attribute in this object,
36110     * they take priority.
36111     */
36112     /**
36113     * @cfg {Object} uiProviders (optional) An object containing properties which
36114     * 
36115     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36116     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36117     * <i>uiProvider</i> attribute of a returned child node is a string rather
36118     * than a reference to a TreeNodeUI implementation, this that string value
36119     * is used as a property name in the uiProviders object. You can define the provider named
36120     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36121     */
36122     uiProviders : {},
36123
36124     /**
36125     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36126     * child nodes before loading.
36127     */
36128     clearOnLoad : true,
36129
36130     /**
36131     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36132     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36133     * Grid query { data : [ .....] }
36134     */
36135     
36136     root : false,
36137      /**
36138     * @cfg {String} queryParam (optional) 
36139     * Name of the query as it will be passed on the querystring (defaults to 'node')
36140     * eg. the request will be ?node=[id]
36141     */
36142     
36143     
36144     queryParam: false,
36145     
36146     /**
36147      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36148      * This is called automatically when a node is expanded, but may be used to reload
36149      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36150      * @param {Roo.tree.TreeNode} node
36151      * @param {Function} callback
36152      */
36153     load : function(node, callback){
36154         if(this.clearOnLoad){
36155             while(node.firstChild){
36156                 node.removeChild(node.firstChild);
36157             }
36158         }
36159         if(node.attributes.children){ // preloaded json children
36160             var cs = node.attributes.children;
36161             for(var i = 0, len = cs.length; i < len; i++){
36162                 node.appendChild(this.createNode(cs[i]));
36163             }
36164             if(typeof callback == "function"){
36165                 callback();
36166             }
36167         }else if(this.dataUrl){
36168             this.requestData(node, callback);
36169         }
36170     },
36171
36172     getParams: function(node){
36173         var buf = [], bp = this.baseParams;
36174         for(var key in bp){
36175             if(typeof bp[key] != "function"){
36176                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36177             }
36178         }
36179         var n = this.queryParam === false ? 'node' : this.queryParam;
36180         buf.push(n + "=", encodeURIComponent(node.id));
36181         return buf.join("");
36182     },
36183
36184     requestData : function(node, callback){
36185         if(this.fireEvent("beforeload", this, node, callback) !== false){
36186             this.transId = Roo.Ajax.request({
36187                 method:this.requestMethod,
36188                 url: this.dataUrl||this.url,
36189                 success: this.handleResponse,
36190                 failure: this.handleFailure,
36191                 scope: this,
36192                 argument: {callback: callback, node: node},
36193                 params: this.getParams(node)
36194             });
36195         }else{
36196             // if the load is cancelled, make sure we notify
36197             // the node that we are done
36198             if(typeof callback == "function"){
36199                 callback();
36200             }
36201         }
36202     },
36203
36204     isLoading : function(){
36205         return this.transId ? true : false;
36206     },
36207
36208     abort : function(){
36209         if(this.isLoading()){
36210             Roo.Ajax.abort(this.transId);
36211         }
36212     },
36213
36214     // private
36215     createNode : function(attr)
36216     {
36217         // apply baseAttrs, nice idea Corey!
36218         if(this.baseAttrs){
36219             Roo.applyIf(attr, this.baseAttrs);
36220         }
36221         if(this.applyLoader !== false){
36222             attr.loader = this;
36223         }
36224         // uiProvider = depreciated..
36225         
36226         if(typeof(attr.uiProvider) == 'string'){
36227            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36228                 /**  eval:var:attr */ eval(attr.uiProvider);
36229         }
36230         if(typeof(this.uiProviders['default']) != 'undefined') {
36231             attr.uiProvider = this.uiProviders['default'];
36232         }
36233         
36234         this.fireEvent('create', this, attr);
36235         
36236         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36237         return(attr.leaf ?
36238                         new Roo.tree.TreeNode(attr) :
36239                         new Roo.tree.AsyncTreeNode(attr));
36240     },
36241
36242     processResponse : function(response, node, callback)
36243     {
36244         var json = response.responseText;
36245         try {
36246             
36247             var o = Roo.decode(json);
36248             
36249             if (this.root === false && typeof(o.success) != undefined) {
36250                 this.root = 'data'; // the default behaviour for list like data..
36251                 }
36252                 
36253             if (this.root !== false &&  !o.success) {
36254                 // it's a failure condition.
36255                 var a = response.argument;
36256                 this.fireEvent("loadexception", this, a.node, response);
36257                 Roo.log("Load failed - should have a handler really");
36258                 return;
36259             }
36260             
36261             
36262             
36263             if (this.root !== false) {
36264                  o = o[this.root];
36265             }
36266             
36267             for(var i = 0, len = o.length; i < len; i++){
36268                 var n = this.createNode(o[i]);
36269                 if(n){
36270                     node.appendChild(n);
36271                 }
36272             }
36273             if(typeof callback == "function"){
36274                 callback(this, node);
36275             }
36276         }catch(e){
36277             this.handleFailure(response);
36278         }
36279     },
36280
36281     handleResponse : function(response){
36282         this.transId = false;
36283         var a = response.argument;
36284         this.processResponse(response, a.node, a.callback);
36285         this.fireEvent("load", this, a.node, response);
36286     },
36287
36288     handleFailure : function(response)
36289     {
36290         // should handle failure better..
36291         this.transId = false;
36292         var a = response.argument;
36293         this.fireEvent("loadexception", this, a.node, response);
36294         if(typeof a.callback == "function"){
36295             a.callback(this, a.node);
36296         }
36297     }
36298 });/*
36299  * Based on:
36300  * Ext JS Library 1.1.1
36301  * Copyright(c) 2006-2007, Ext JS, LLC.
36302  *
36303  * Originally Released Under LGPL - original licence link has changed is not relivant.
36304  *
36305  * Fork - LGPL
36306  * <script type="text/javascript">
36307  */
36308
36309 /**
36310 * @class Roo.tree.TreeFilter
36311 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36312 * @param {TreePanel} tree
36313 * @param {Object} config (optional)
36314  */
36315 Roo.tree.TreeFilter = function(tree, config){
36316     this.tree = tree;
36317     this.filtered = {};
36318     Roo.apply(this, config);
36319 };
36320
36321 Roo.tree.TreeFilter.prototype = {
36322     clearBlank:false,
36323     reverse:false,
36324     autoClear:false,
36325     remove:false,
36326
36327      /**
36328      * Filter the data by a specific attribute.
36329      * @param {String/RegExp} value Either string that the attribute value
36330      * should start with or a RegExp to test against the attribute
36331      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36332      * @param {TreeNode} startNode (optional) The node to start the filter at.
36333      */
36334     filter : function(value, attr, startNode){
36335         attr = attr || "text";
36336         var f;
36337         if(typeof value == "string"){
36338             var vlen = value.length;
36339             // auto clear empty filter
36340             if(vlen == 0 && this.clearBlank){
36341                 this.clear();
36342                 return;
36343             }
36344             value = value.toLowerCase();
36345             f = function(n){
36346                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36347             };
36348         }else if(value.exec){ // regex?
36349             f = function(n){
36350                 return value.test(n.attributes[attr]);
36351             };
36352         }else{
36353             throw 'Illegal filter type, must be string or regex';
36354         }
36355         this.filterBy(f, null, startNode);
36356         },
36357
36358     /**
36359      * Filter by a function. The passed function will be called with each
36360      * node in the tree (or from the startNode). If the function returns true, the node is kept
36361      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36362      * @param {Function} fn The filter function
36363      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36364      */
36365     filterBy : function(fn, scope, startNode){
36366         startNode = startNode || this.tree.root;
36367         if(this.autoClear){
36368             this.clear();
36369         }
36370         var af = this.filtered, rv = this.reverse;
36371         var f = function(n){
36372             if(n == startNode){
36373                 return true;
36374             }
36375             if(af[n.id]){
36376                 return false;
36377             }
36378             var m = fn.call(scope || n, n);
36379             if(!m || rv){
36380                 af[n.id] = n;
36381                 n.ui.hide();
36382                 return false;
36383             }
36384             return true;
36385         };
36386         startNode.cascade(f);
36387         if(this.remove){
36388            for(var id in af){
36389                if(typeof id != "function"){
36390                    var n = af[id];
36391                    if(n && n.parentNode){
36392                        n.parentNode.removeChild(n);
36393                    }
36394                }
36395            }
36396         }
36397     },
36398
36399     /**
36400      * Clears the current filter. Note: with the "remove" option
36401      * set a filter cannot be cleared.
36402      */
36403     clear : function(){
36404         var t = this.tree;
36405         var af = this.filtered;
36406         for(var id in af){
36407             if(typeof id != "function"){
36408                 var n = af[id];
36409                 if(n){
36410                     n.ui.show();
36411                 }
36412             }
36413         }
36414         this.filtered = {};
36415     }
36416 };
36417 /*
36418  * Based on:
36419  * Ext JS Library 1.1.1
36420  * Copyright(c) 2006-2007, Ext JS, LLC.
36421  *
36422  * Originally Released Under LGPL - original licence link has changed is not relivant.
36423  *
36424  * Fork - LGPL
36425  * <script type="text/javascript">
36426  */
36427  
36428
36429 /**
36430  * @class Roo.tree.TreeSorter
36431  * Provides sorting of nodes in a TreePanel
36432  * 
36433  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36434  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36435  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36436  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36437  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36438  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36439  * @constructor
36440  * @param {TreePanel} tree
36441  * @param {Object} config
36442  */
36443 Roo.tree.TreeSorter = function(tree, config){
36444     Roo.apply(this, config);
36445     tree.on("beforechildrenrendered", this.doSort, this);
36446     tree.on("append", this.updateSort, this);
36447     tree.on("insert", this.updateSort, this);
36448     
36449     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36450     var p = this.property || "text";
36451     var sortType = this.sortType;
36452     var fs = this.folderSort;
36453     var cs = this.caseSensitive === true;
36454     var leafAttr = this.leafAttr || 'leaf';
36455
36456     this.sortFn = function(n1, n2){
36457         if(fs){
36458             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36459                 return 1;
36460             }
36461             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36462                 return -1;
36463             }
36464         }
36465         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36466         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36467         if(v1 < v2){
36468                         return dsc ? +1 : -1;
36469                 }else if(v1 > v2){
36470                         return dsc ? -1 : +1;
36471         }else{
36472                 return 0;
36473         }
36474     };
36475 };
36476
36477 Roo.tree.TreeSorter.prototype = {
36478     doSort : function(node){
36479         node.sort(this.sortFn);
36480     },
36481     
36482     compareNodes : function(n1, n2){
36483         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36484     },
36485     
36486     updateSort : function(tree, node){
36487         if(node.childrenRendered){
36488             this.doSort.defer(1, this, [node]);
36489         }
36490     }
36491 };/*
36492  * Based on:
36493  * Ext JS Library 1.1.1
36494  * Copyright(c) 2006-2007, Ext JS, LLC.
36495  *
36496  * Originally Released Under LGPL - original licence link has changed is not relivant.
36497  *
36498  * Fork - LGPL
36499  * <script type="text/javascript">
36500  */
36501
36502 if(Roo.dd.DropZone){
36503     
36504 Roo.tree.TreeDropZone = function(tree, config){
36505     this.allowParentInsert = false;
36506     this.allowContainerDrop = false;
36507     this.appendOnly = false;
36508     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36509     this.tree = tree;
36510     this.lastInsertClass = "x-tree-no-status";
36511     this.dragOverData = {};
36512 };
36513
36514 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36515     ddGroup : "TreeDD",
36516     scroll:  true,
36517     
36518     expandDelay : 1000,
36519     
36520     expandNode : function(node){
36521         if(node.hasChildNodes() && !node.isExpanded()){
36522             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36523         }
36524     },
36525     
36526     queueExpand : function(node){
36527         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36528     },
36529     
36530     cancelExpand : function(){
36531         if(this.expandProcId){
36532             clearTimeout(this.expandProcId);
36533             this.expandProcId = false;
36534         }
36535     },
36536     
36537     isValidDropPoint : function(n, pt, dd, e, data){
36538         if(!n || !data){ return false; }
36539         var targetNode = n.node;
36540         var dropNode = data.node;
36541         // default drop rules
36542         if(!(targetNode && targetNode.isTarget && pt)){
36543             return false;
36544         }
36545         if(pt == "append" && targetNode.allowChildren === false){
36546             return false;
36547         }
36548         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36549             return false;
36550         }
36551         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36552             return false;
36553         }
36554         // reuse the object
36555         var overEvent = this.dragOverData;
36556         overEvent.tree = this.tree;
36557         overEvent.target = targetNode;
36558         overEvent.data = data;
36559         overEvent.point = pt;
36560         overEvent.source = dd;
36561         overEvent.rawEvent = e;
36562         overEvent.dropNode = dropNode;
36563         overEvent.cancel = false;  
36564         var result = this.tree.fireEvent("nodedragover", overEvent);
36565         return overEvent.cancel === false && result !== false;
36566     },
36567     
36568     getDropPoint : function(e, n, dd)
36569     {
36570         var tn = n.node;
36571         if(tn.isRoot){
36572             return tn.allowChildren !== false ? "append" : false; // always append for root
36573         }
36574         var dragEl = n.ddel;
36575         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36576         var y = Roo.lib.Event.getPageY(e);
36577         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36578         
36579         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36580         var noAppend = tn.allowChildren === false;
36581         if(this.appendOnly || tn.parentNode.allowChildren === false){
36582             return noAppend ? false : "append";
36583         }
36584         var noBelow = false;
36585         if(!this.allowParentInsert){
36586             noBelow = tn.hasChildNodes() && tn.isExpanded();
36587         }
36588         var q = (b - t) / (noAppend ? 2 : 3);
36589         if(y >= t && y < (t + q)){
36590             return "above";
36591         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36592             return "below";
36593         }else{
36594             return "append";
36595         }
36596     },
36597     
36598     onNodeEnter : function(n, dd, e, data)
36599     {
36600         this.cancelExpand();
36601     },
36602     
36603     onNodeOver : function(n, dd, e, data)
36604     {
36605        
36606         var pt = this.getDropPoint(e, n, dd);
36607         var node = n.node;
36608         
36609         // auto node expand check
36610         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36611             this.queueExpand(node);
36612         }else if(pt != "append"){
36613             this.cancelExpand();
36614         }
36615         
36616         // set the insert point style on the target node
36617         var returnCls = this.dropNotAllowed;
36618         if(this.isValidDropPoint(n, pt, dd, e, data)){
36619            if(pt){
36620                var el = n.ddel;
36621                var cls;
36622                if(pt == "above"){
36623                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36624                    cls = "x-tree-drag-insert-above";
36625                }else if(pt == "below"){
36626                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36627                    cls = "x-tree-drag-insert-below";
36628                }else{
36629                    returnCls = "x-tree-drop-ok-append";
36630                    cls = "x-tree-drag-append";
36631                }
36632                if(this.lastInsertClass != cls){
36633                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36634                    this.lastInsertClass = cls;
36635                }
36636            }
36637        }
36638        return returnCls;
36639     },
36640     
36641     onNodeOut : function(n, dd, e, data){
36642         
36643         this.cancelExpand();
36644         this.removeDropIndicators(n);
36645     },
36646     
36647     onNodeDrop : function(n, dd, e, data){
36648         var point = this.getDropPoint(e, n, dd);
36649         var targetNode = n.node;
36650         targetNode.ui.startDrop();
36651         if(!this.isValidDropPoint(n, point, dd, e, data)){
36652             targetNode.ui.endDrop();
36653             return false;
36654         }
36655         // first try to find the drop node
36656         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36657         var dropEvent = {
36658             tree : this.tree,
36659             target: targetNode,
36660             data: data,
36661             point: point,
36662             source: dd,
36663             rawEvent: e,
36664             dropNode: dropNode,
36665             cancel: !dropNode   
36666         };
36667         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36668         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36669             targetNode.ui.endDrop();
36670             return false;
36671         }
36672         // allow target changing
36673         targetNode = dropEvent.target;
36674         if(point == "append" && !targetNode.isExpanded()){
36675             targetNode.expand(false, null, function(){
36676                 this.completeDrop(dropEvent);
36677             }.createDelegate(this));
36678         }else{
36679             this.completeDrop(dropEvent);
36680         }
36681         return true;
36682     },
36683     
36684     completeDrop : function(de){
36685         var ns = de.dropNode, p = de.point, t = de.target;
36686         if(!(ns instanceof Array)){
36687             ns = [ns];
36688         }
36689         var n;
36690         for(var i = 0, len = ns.length; i < len; i++){
36691             n = ns[i];
36692             if(p == "above"){
36693                 t.parentNode.insertBefore(n, t);
36694             }else if(p == "below"){
36695                 t.parentNode.insertBefore(n, t.nextSibling);
36696             }else{
36697                 t.appendChild(n);
36698             }
36699         }
36700         n.ui.focus();
36701         if(this.tree.hlDrop){
36702             n.ui.highlight();
36703         }
36704         t.ui.endDrop();
36705         this.tree.fireEvent("nodedrop", de);
36706     },
36707     
36708     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36709         if(this.tree.hlDrop){
36710             dropNode.ui.focus();
36711             dropNode.ui.highlight();
36712         }
36713         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36714     },
36715     
36716     getTree : function(){
36717         return this.tree;
36718     },
36719     
36720     removeDropIndicators : function(n){
36721         if(n && n.ddel){
36722             var el = n.ddel;
36723             Roo.fly(el).removeClass([
36724                     "x-tree-drag-insert-above",
36725                     "x-tree-drag-insert-below",
36726                     "x-tree-drag-append"]);
36727             this.lastInsertClass = "_noclass";
36728         }
36729     },
36730     
36731     beforeDragDrop : function(target, e, id){
36732         this.cancelExpand();
36733         return true;
36734     },
36735     
36736     afterRepair : function(data){
36737         if(data && Roo.enableFx){
36738             data.node.ui.highlight();
36739         }
36740         this.hideProxy();
36741     } 
36742     
36743 });
36744
36745 }
36746 /*
36747  * Based on:
36748  * Ext JS Library 1.1.1
36749  * Copyright(c) 2006-2007, Ext JS, LLC.
36750  *
36751  * Originally Released Under LGPL - original licence link has changed is not relivant.
36752  *
36753  * Fork - LGPL
36754  * <script type="text/javascript">
36755  */
36756  
36757
36758 if(Roo.dd.DragZone){
36759 Roo.tree.TreeDragZone = function(tree, config){
36760     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36761     this.tree = tree;
36762 };
36763
36764 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36765     ddGroup : "TreeDD",
36766    
36767     onBeforeDrag : function(data, e){
36768         var n = data.node;
36769         return n && n.draggable && !n.disabled;
36770     },
36771      
36772     
36773     onInitDrag : function(e){
36774         var data = this.dragData;
36775         this.tree.getSelectionModel().select(data.node);
36776         this.proxy.update("");
36777         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36778         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36779     },
36780     
36781     getRepairXY : function(e, data){
36782         return data.node.ui.getDDRepairXY();
36783     },
36784     
36785     onEndDrag : function(data, e){
36786         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36787         
36788         
36789     },
36790     
36791     onValidDrop : function(dd, e, id){
36792         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36793         this.hideProxy();
36794     },
36795     
36796     beforeInvalidDrop : function(e, id){
36797         // this scrolls the original position back into view
36798         var sm = this.tree.getSelectionModel();
36799         sm.clearSelections();
36800         sm.select(this.dragData.node);
36801     }
36802 });
36803 }/*
36804  * Based on:
36805  * Ext JS Library 1.1.1
36806  * Copyright(c) 2006-2007, Ext JS, LLC.
36807  *
36808  * Originally Released Under LGPL - original licence link has changed is not relivant.
36809  *
36810  * Fork - LGPL
36811  * <script type="text/javascript">
36812  */
36813 /**
36814  * @class Roo.tree.TreeEditor
36815  * @extends Roo.Editor
36816  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36817  * as the editor field.
36818  * @constructor
36819  * @param {Object} config (used to be the tree panel.)
36820  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36821  * 
36822  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36823  * @cfg {Roo.form.TextField|Object} field The field configuration
36824  *
36825  * 
36826  */
36827 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36828     var tree = config;
36829     var field;
36830     if (oldconfig) { // old style..
36831         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36832     } else {
36833         // new style..
36834         tree = config.tree;
36835         config.field = config.field  || {};
36836         config.field.xtype = 'TextField';
36837         field = Roo.factory(config.field, Roo.form);
36838     }
36839     config = config || {};
36840     
36841     
36842     this.addEvents({
36843         /**
36844          * @event beforenodeedit
36845          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36846          * false from the handler of this event.
36847          * @param {Editor} this
36848          * @param {Roo.tree.Node} node 
36849          */
36850         "beforenodeedit" : true
36851     });
36852     
36853     //Roo.log(config);
36854     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36855
36856     this.tree = tree;
36857
36858     tree.on('beforeclick', this.beforeNodeClick, this);
36859     tree.getTreeEl().on('mousedown', this.hide, this);
36860     this.on('complete', this.updateNode, this);
36861     this.on('beforestartedit', this.fitToTree, this);
36862     this.on('startedit', this.bindScroll, this, {delay:10});
36863     this.on('specialkey', this.onSpecialKey, this);
36864 };
36865
36866 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36867     /**
36868      * @cfg {String} alignment
36869      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36870      */
36871     alignment: "l-l",
36872     // inherit
36873     autoSize: false,
36874     /**
36875      * @cfg {Boolean} hideEl
36876      * True to hide the bound element while the editor is displayed (defaults to false)
36877      */
36878     hideEl : false,
36879     /**
36880      * @cfg {String} cls
36881      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36882      */
36883     cls: "x-small-editor x-tree-editor",
36884     /**
36885      * @cfg {Boolean} shim
36886      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36887      */
36888     shim:false,
36889     // inherit
36890     shadow:"frame",
36891     /**
36892      * @cfg {Number} maxWidth
36893      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36894      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36895      * scroll and client offsets into account prior to each edit.
36896      */
36897     maxWidth: 250,
36898
36899     editDelay : 350,
36900
36901     // private
36902     fitToTree : function(ed, el){
36903         var td = this.tree.getTreeEl().dom, nd = el.dom;
36904         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36905             td.scrollLeft = nd.offsetLeft;
36906         }
36907         var w = Math.min(
36908                 this.maxWidth,
36909                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36910         this.setSize(w, '');
36911         
36912         return this.fireEvent('beforenodeedit', this, this.editNode);
36913         
36914     },
36915
36916     // private
36917     triggerEdit : function(node){
36918         this.completeEdit();
36919         this.editNode = node;
36920         this.startEdit(node.ui.textNode, node.text);
36921     },
36922
36923     // private
36924     bindScroll : function(){
36925         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36926     },
36927
36928     // private
36929     beforeNodeClick : function(node, e){
36930         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36931         this.lastClick = new Date();
36932         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36933             e.stopEvent();
36934             this.triggerEdit(node);
36935             return false;
36936         }
36937         return true;
36938     },
36939
36940     // private
36941     updateNode : function(ed, value){
36942         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36943         this.editNode.setText(value);
36944     },
36945
36946     // private
36947     onHide : function(){
36948         Roo.tree.TreeEditor.superclass.onHide.call(this);
36949         if(this.editNode){
36950             this.editNode.ui.focus();
36951         }
36952     },
36953
36954     // private
36955     onSpecialKey : function(field, e){
36956         var k = e.getKey();
36957         if(k == e.ESC){
36958             e.stopEvent();
36959             this.cancelEdit();
36960         }else if(k == e.ENTER && !e.hasModifier()){
36961             e.stopEvent();
36962             this.completeEdit();
36963         }
36964     }
36965 });//<Script type="text/javascript">
36966 /*
36967  * Based on:
36968  * Ext JS Library 1.1.1
36969  * Copyright(c) 2006-2007, Ext JS, LLC.
36970  *
36971  * Originally Released Under LGPL - original licence link has changed is not relivant.
36972  *
36973  * Fork - LGPL
36974  * <script type="text/javascript">
36975  */
36976  
36977 /**
36978  * Not documented??? - probably should be...
36979  */
36980
36981 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36982     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36983     
36984     renderElements : function(n, a, targetNode, bulkRender){
36985         //consel.log("renderElements?");
36986         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36987
36988         var t = n.getOwnerTree();
36989         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36990         
36991         var cols = t.columns;
36992         var bw = t.borderWidth;
36993         var c = cols[0];
36994         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36995          var cb = typeof a.checked == "boolean";
36996         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36997         var colcls = 'x-t-' + tid + '-c0';
36998         var buf = [
36999             '<li class="x-tree-node">',
37000             
37001                 
37002                 '<div class="x-tree-node-el ', a.cls,'">',
37003                     // extran...
37004                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37005                 
37006                 
37007                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37008                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37009                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37010                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37011                            (a.iconCls ? ' '+a.iconCls : ''),
37012                            '" unselectable="on" />',
37013                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37014                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37015                              
37016                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37017                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37018                             '<span unselectable="on" qtip="' + tx + '">',
37019                              tx,
37020                              '</span></a>' ,
37021                     '</div>',
37022                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37023                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37024                  ];
37025         for(var i = 1, len = cols.length; i < len; i++){
37026             c = cols[i];
37027             colcls = 'x-t-' + tid + '-c' +i;
37028             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37029             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37030                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37031                       "</div>");
37032          }
37033          
37034          buf.push(
37035             '</a>',
37036             '<div class="x-clear"></div></div>',
37037             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37038             "</li>");
37039         
37040         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37041             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37042                                 n.nextSibling.ui.getEl(), buf.join(""));
37043         }else{
37044             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37045         }
37046         var el = this.wrap.firstChild;
37047         this.elRow = el;
37048         this.elNode = el.firstChild;
37049         this.ranchor = el.childNodes[1];
37050         this.ctNode = this.wrap.childNodes[1];
37051         var cs = el.firstChild.childNodes;
37052         this.indentNode = cs[0];
37053         this.ecNode = cs[1];
37054         this.iconNode = cs[2];
37055         var index = 3;
37056         if(cb){
37057             this.checkbox = cs[3];
37058             index++;
37059         }
37060         this.anchor = cs[index];
37061         
37062         this.textNode = cs[index].firstChild;
37063         
37064         //el.on("click", this.onClick, this);
37065         //el.on("dblclick", this.onDblClick, this);
37066         
37067         
37068        // console.log(this);
37069     },
37070     initEvents : function(){
37071         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37072         
37073             
37074         var a = this.ranchor;
37075
37076         var el = Roo.get(a);
37077
37078         if(Roo.isOpera){ // opera render bug ignores the CSS
37079             el.setStyle("text-decoration", "none");
37080         }
37081
37082         el.on("click", this.onClick, this);
37083         el.on("dblclick", this.onDblClick, this);
37084         el.on("contextmenu", this.onContextMenu, this);
37085         
37086     },
37087     
37088     /*onSelectedChange : function(state){
37089         if(state){
37090             this.focus();
37091             this.addClass("x-tree-selected");
37092         }else{
37093             //this.blur();
37094             this.removeClass("x-tree-selected");
37095         }
37096     },*/
37097     addClass : function(cls){
37098         if(this.elRow){
37099             Roo.fly(this.elRow).addClass(cls);
37100         }
37101         
37102     },
37103     
37104     
37105     removeClass : function(cls){
37106         if(this.elRow){
37107             Roo.fly(this.elRow).removeClass(cls);
37108         }
37109     }
37110
37111     
37112     
37113 });//<Script type="text/javascript">
37114
37115 /*
37116  * Based on:
37117  * Ext JS Library 1.1.1
37118  * Copyright(c) 2006-2007, Ext JS, LLC.
37119  *
37120  * Originally Released Under LGPL - original licence link has changed is not relivant.
37121  *
37122  * Fork - LGPL
37123  * <script type="text/javascript">
37124  */
37125  
37126
37127 /**
37128  * @class Roo.tree.ColumnTree
37129  * @extends Roo.data.TreePanel
37130  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37131  * @cfg {int} borderWidth  compined right/left border allowance
37132  * @constructor
37133  * @param {String/HTMLElement/Element} el The container element
37134  * @param {Object} config
37135  */
37136 Roo.tree.ColumnTree =  function(el, config)
37137 {
37138    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37139    this.addEvents({
37140         /**
37141         * @event resize
37142         * Fire this event on a container when it resizes
37143         * @param {int} w Width
37144         * @param {int} h Height
37145         */
37146        "resize" : true
37147     });
37148     this.on('resize', this.onResize, this);
37149 };
37150
37151 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37152     //lines:false,
37153     
37154     
37155     borderWidth: Roo.isBorderBox ? 0 : 2, 
37156     headEls : false,
37157     
37158     render : function(){
37159         // add the header.....
37160        
37161         Roo.tree.ColumnTree.superclass.render.apply(this);
37162         
37163         this.el.addClass('x-column-tree');
37164         
37165         this.headers = this.el.createChild(
37166             {cls:'x-tree-headers'},this.innerCt.dom);
37167    
37168         var cols = this.columns, c;
37169         var totalWidth = 0;
37170         this.headEls = [];
37171         var  len = cols.length;
37172         for(var i = 0; i < len; i++){
37173              c = cols[i];
37174              totalWidth += c.width;
37175             this.headEls.push(this.headers.createChild({
37176                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37177                  cn: {
37178                      cls:'x-tree-hd-text',
37179                      html: c.header
37180                  },
37181                  style:'width:'+(c.width-this.borderWidth)+'px;'
37182              }));
37183         }
37184         this.headers.createChild({cls:'x-clear'});
37185         // prevent floats from wrapping when clipped
37186         this.headers.setWidth(totalWidth);
37187         //this.innerCt.setWidth(totalWidth);
37188         this.innerCt.setStyle({ overflow: 'auto' });
37189         this.onResize(this.width, this.height);
37190              
37191         
37192     },
37193     onResize : function(w,h)
37194     {
37195         this.height = h;
37196         this.width = w;
37197         // resize cols..
37198         this.innerCt.setWidth(this.width);
37199         this.innerCt.setHeight(this.height-20);
37200         
37201         // headers...
37202         var cols = this.columns, c;
37203         var totalWidth = 0;
37204         var expEl = false;
37205         var len = cols.length;
37206         for(var i = 0; i < len; i++){
37207             c = cols[i];
37208             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37209                 // it's the expander..
37210                 expEl  = this.headEls[i];
37211                 continue;
37212             }
37213             totalWidth += c.width;
37214             
37215         }
37216         if (expEl) {
37217             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37218         }
37219         this.headers.setWidth(w-20);
37220
37221         
37222         
37223         
37224     }
37225 });
37226 /*
37227  * Based on:
37228  * Ext JS Library 1.1.1
37229  * Copyright(c) 2006-2007, Ext JS, LLC.
37230  *
37231  * Originally Released Under LGPL - original licence link has changed is not relivant.
37232  *
37233  * Fork - LGPL
37234  * <script type="text/javascript">
37235  */
37236  
37237 /**
37238  * @class Roo.menu.Menu
37239  * @extends Roo.util.Observable
37240  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37241  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37242  * @constructor
37243  * Creates a new Menu
37244  * @param {Object} config Configuration options
37245  */
37246 Roo.menu.Menu = function(config){
37247     
37248     Roo.menu.Menu.superclass.constructor.call(this, config);
37249     
37250     this.id = this.id || Roo.id();
37251     this.addEvents({
37252         /**
37253          * @event beforeshow
37254          * Fires before this menu is displayed
37255          * @param {Roo.menu.Menu} this
37256          */
37257         beforeshow : true,
37258         /**
37259          * @event beforehide
37260          * Fires before this menu is hidden
37261          * @param {Roo.menu.Menu} this
37262          */
37263         beforehide : true,
37264         /**
37265          * @event show
37266          * Fires after this menu is displayed
37267          * @param {Roo.menu.Menu} this
37268          */
37269         show : true,
37270         /**
37271          * @event hide
37272          * Fires after this menu is hidden
37273          * @param {Roo.menu.Menu} this
37274          */
37275         hide : true,
37276         /**
37277          * @event click
37278          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37279          * @param {Roo.menu.Menu} this
37280          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37281          * @param {Roo.EventObject} e
37282          */
37283         click : true,
37284         /**
37285          * @event mouseover
37286          * Fires when the mouse is hovering over this menu
37287          * @param {Roo.menu.Menu} this
37288          * @param {Roo.EventObject} e
37289          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37290          */
37291         mouseover : true,
37292         /**
37293          * @event mouseout
37294          * Fires when the mouse exits this menu
37295          * @param {Roo.menu.Menu} this
37296          * @param {Roo.EventObject} e
37297          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37298          */
37299         mouseout : true,
37300         /**
37301          * @event itemclick
37302          * Fires when a menu item contained in this menu is clicked
37303          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37304          * @param {Roo.EventObject} e
37305          */
37306         itemclick: true
37307     });
37308     if (this.registerMenu) {
37309         Roo.menu.MenuMgr.register(this);
37310     }
37311     
37312     var mis = this.items;
37313     this.items = new Roo.util.MixedCollection();
37314     if(mis){
37315         this.add.apply(this, mis);
37316     }
37317 };
37318
37319 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37320     /**
37321      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37322      */
37323     minWidth : 120,
37324     /**
37325      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37326      * for bottom-right shadow (defaults to "sides")
37327      */
37328     shadow : "sides",
37329     /**
37330      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37331      * this menu (defaults to "tl-tr?")
37332      */
37333     subMenuAlign : "tl-tr?",
37334     /**
37335      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37336      * relative to its element of origin (defaults to "tl-bl?")
37337      */
37338     defaultAlign : "tl-bl?",
37339     /**
37340      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37341      */
37342     allowOtherMenus : false,
37343     /**
37344      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37345      */
37346     registerMenu : true,
37347
37348     hidden:true,
37349
37350     // private
37351     render : function(){
37352         if(this.el){
37353             return;
37354         }
37355         var el = this.el = new Roo.Layer({
37356             cls: "x-menu",
37357             shadow:this.shadow,
37358             constrain: false,
37359             parentEl: this.parentEl || document.body,
37360             zindex:15000
37361         });
37362
37363         this.keyNav = new Roo.menu.MenuNav(this);
37364
37365         if(this.plain){
37366             el.addClass("x-menu-plain");
37367         }
37368         if(this.cls){
37369             el.addClass(this.cls);
37370         }
37371         // generic focus element
37372         this.focusEl = el.createChild({
37373             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37374         });
37375         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37376         //disabling touch- as it's causing issues ..
37377         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37378         ul.on('click'   , this.onClick, this);
37379         
37380         
37381         ul.on("mouseover", this.onMouseOver, this);
37382         ul.on("mouseout", this.onMouseOut, this);
37383         this.items.each(function(item){
37384             if (item.hidden) {
37385                 return;
37386             }
37387             
37388             var li = document.createElement("li");
37389             li.className = "x-menu-list-item";
37390             ul.dom.appendChild(li);
37391             item.render(li, this);
37392         }, this);
37393         this.ul = ul;
37394         this.autoWidth();
37395     },
37396
37397     // private
37398     autoWidth : function(){
37399         var el = this.el, ul = this.ul;
37400         if(!el){
37401             return;
37402         }
37403         var w = this.width;
37404         if(w){
37405             el.setWidth(w);
37406         }else if(Roo.isIE){
37407             el.setWidth(this.minWidth);
37408             var t = el.dom.offsetWidth; // force recalc
37409             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37410         }
37411     },
37412
37413     // private
37414     delayAutoWidth : function(){
37415         if(this.rendered){
37416             if(!this.awTask){
37417                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37418             }
37419             this.awTask.delay(20);
37420         }
37421     },
37422
37423     // private
37424     findTargetItem : function(e){
37425         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37426         if(t && t.menuItemId){
37427             return this.items.get(t.menuItemId);
37428         }
37429     },
37430
37431     // private
37432     onClick : function(e){
37433         Roo.log("menu.onClick");
37434         var t = this.findTargetItem(e);
37435         if(!t){
37436             return;
37437         }
37438         Roo.log(e);
37439         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37440             if(t == this.activeItem && t.shouldDeactivate(e)){
37441                 this.activeItem.deactivate();
37442                 delete this.activeItem;
37443                 return;
37444             }
37445             if(t.canActivate){
37446                 this.setActiveItem(t, true);
37447             }
37448             return;
37449             
37450             
37451         }
37452         
37453         t.onClick(e);
37454         this.fireEvent("click", this, t, e);
37455     },
37456
37457     // private
37458     setActiveItem : function(item, autoExpand){
37459         if(item != this.activeItem){
37460             if(this.activeItem){
37461                 this.activeItem.deactivate();
37462             }
37463             this.activeItem = item;
37464             item.activate(autoExpand);
37465         }else if(autoExpand){
37466             item.expandMenu();
37467         }
37468     },
37469
37470     // private
37471     tryActivate : function(start, step){
37472         var items = this.items;
37473         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37474             var item = items.get(i);
37475             if(!item.disabled && item.canActivate){
37476                 this.setActiveItem(item, false);
37477                 return item;
37478             }
37479         }
37480         return false;
37481     },
37482
37483     // private
37484     onMouseOver : function(e){
37485         var t;
37486         if(t = this.findTargetItem(e)){
37487             if(t.canActivate && !t.disabled){
37488                 this.setActiveItem(t, true);
37489             }
37490         }
37491         this.fireEvent("mouseover", this, e, t);
37492     },
37493
37494     // private
37495     onMouseOut : function(e){
37496         var t;
37497         if(t = this.findTargetItem(e)){
37498             if(t == this.activeItem && t.shouldDeactivate(e)){
37499                 this.activeItem.deactivate();
37500                 delete this.activeItem;
37501             }
37502         }
37503         this.fireEvent("mouseout", this, e, t);
37504     },
37505
37506     /**
37507      * Read-only.  Returns true if the menu is currently displayed, else false.
37508      * @type Boolean
37509      */
37510     isVisible : function(){
37511         return this.el && !this.hidden;
37512     },
37513
37514     /**
37515      * Displays this menu relative to another element
37516      * @param {String/HTMLElement/Roo.Element} element The element to align to
37517      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37518      * the element (defaults to this.defaultAlign)
37519      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37520      */
37521     show : function(el, pos, parentMenu){
37522         this.parentMenu = parentMenu;
37523         if(!this.el){
37524             this.render();
37525         }
37526         this.fireEvent("beforeshow", this);
37527         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37528     },
37529
37530     /**
37531      * Displays this menu at a specific xy position
37532      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37533      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37534      */
37535     showAt : function(xy, parentMenu, /* private: */_e){
37536         this.parentMenu = parentMenu;
37537         if(!this.el){
37538             this.render();
37539         }
37540         if(_e !== false){
37541             this.fireEvent("beforeshow", this);
37542             xy = this.el.adjustForConstraints(xy);
37543         }
37544         this.el.setXY(xy);
37545         this.el.show();
37546         this.hidden = false;
37547         this.focus();
37548         this.fireEvent("show", this);
37549     },
37550
37551     focus : function(){
37552         if(!this.hidden){
37553             this.doFocus.defer(50, this);
37554         }
37555     },
37556
37557     doFocus : function(){
37558         if(!this.hidden){
37559             this.focusEl.focus();
37560         }
37561     },
37562
37563     /**
37564      * Hides this menu and optionally all parent menus
37565      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37566      */
37567     hide : function(deep){
37568         if(this.el && this.isVisible()){
37569             this.fireEvent("beforehide", this);
37570             if(this.activeItem){
37571                 this.activeItem.deactivate();
37572                 this.activeItem = null;
37573             }
37574             this.el.hide();
37575             this.hidden = true;
37576             this.fireEvent("hide", this);
37577         }
37578         if(deep === true && this.parentMenu){
37579             this.parentMenu.hide(true);
37580         }
37581     },
37582
37583     /**
37584      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37585      * Any of the following are valid:
37586      * <ul>
37587      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37588      * <li>An HTMLElement object which will be converted to a menu item</li>
37589      * <li>A menu item config object that will be created as a new menu item</li>
37590      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37591      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37592      * </ul>
37593      * Usage:
37594      * <pre><code>
37595 // Create the menu
37596 var menu = new Roo.menu.Menu();
37597
37598 // Create a menu item to add by reference
37599 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37600
37601 // Add a bunch of items at once using different methods.
37602 // Only the last item added will be returned.
37603 var item = menu.add(
37604     menuItem,                // add existing item by ref
37605     'Dynamic Item',          // new TextItem
37606     '-',                     // new separator
37607     { text: 'Config Item' }  // new item by config
37608 );
37609 </code></pre>
37610      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37611      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37612      */
37613     add : function(){
37614         var a = arguments, l = a.length, item;
37615         for(var i = 0; i < l; i++){
37616             var el = a[i];
37617             if ((typeof(el) == "object") && el.xtype && el.xns) {
37618                 el = Roo.factory(el, Roo.menu);
37619             }
37620             
37621             if(el.render){ // some kind of Item
37622                 item = this.addItem(el);
37623             }else if(typeof el == "string"){ // string
37624                 if(el == "separator" || el == "-"){
37625                     item = this.addSeparator();
37626                 }else{
37627                     item = this.addText(el);
37628                 }
37629             }else if(el.tagName || el.el){ // element
37630                 item = this.addElement(el);
37631             }else if(typeof el == "object"){ // must be menu item config?
37632                 item = this.addMenuItem(el);
37633             }
37634         }
37635         return item;
37636     },
37637
37638     /**
37639      * Returns this menu's underlying {@link Roo.Element} object
37640      * @return {Roo.Element} The element
37641      */
37642     getEl : function(){
37643         if(!this.el){
37644             this.render();
37645         }
37646         return this.el;
37647     },
37648
37649     /**
37650      * Adds a separator bar to the menu
37651      * @return {Roo.menu.Item} The menu item that was added
37652      */
37653     addSeparator : function(){
37654         return this.addItem(new Roo.menu.Separator());
37655     },
37656
37657     /**
37658      * Adds an {@link Roo.Element} object to the menu
37659      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37660      * @return {Roo.menu.Item} The menu item that was added
37661      */
37662     addElement : function(el){
37663         return this.addItem(new Roo.menu.BaseItem(el));
37664     },
37665
37666     /**
37667      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37668      * @param {Roo.menu.Item} item The menu item to add
37669      * @return {Roo.menu.Item} The menu item that was added
37670      */
37671     addItem : function(item){
37672         this.items.add(item);
37673         if(this.ul){
37674             var li = document.createElement("li");
37675             li.className = "x-menu-list-item";
37676             this.ul.dom.appendChild(li);
37677             item.render(li, this);
37678             this.delayAutoWidth();
37679         }
37680         return item;
37681     },
37682
37683     /**
37684      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37685      * @param {Object} config A MenuItem config object
37686      * @return {Roo.menu.Item} The menu item that was added
37687      */
37688     addMenuItem : function(config){
37689         if(!(config instanceof Roo.menu.Item)){
37690             if(typeof config.checked == "boolean"){ // must be check menu item config?
37691                 config = new Roo.menu.CheckItem(config);
37692             }else{
37693                 config = new Roo.menu.Item(config);
37694             }
37695         }
37696         return this.addItem(config);
37697     },
37698
37699     /**
37700      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37701      * @param {String} text The text to display in the menu item
37702      * @return {Roo.menu.Item} The menu item that was added
37703      */
37704     addText : function(text){
37705         return this.addItem(new Roo.menu.TextItem({ text : text }));
37706     },
37707
37708     /**
37709      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37710      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37711      * @param {Roo.menu.Item} item The menu item to add
37712      * @return {Roo.menu.Item} The menu item that was added
37713      */
37714     insert : function(index, item){
37715         this.items.insert(index, item);
37716         if(this.ul){
37717             var li = document.createElement("li");
37718             li.className = "x-menu-list-item";
37719             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37720             item.render(li, this);
37721             this.delayAutoWidth();
37722         }
37723         return item;
37724     },
37725
37726     /**
37727      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37728      * @param {Roo.menu.Item} item The menu item to remove
37729      */
37730     remove : function(item){
37731         this.items.removeKey(item.id);
37732         item.destroy();
37733     },
37734
37735     /**
37736      * Removes and destroys all items in the menu
37737      */
37738     removeAll : function(){
37739         var f;
37740         while(f = this.items.first()){
37741             this.remove(f);
37742         }
37743     }
37744 });
37745
37746 // MenuNav is a private utility class used internally by the Menu
37747 Roo.menu.MenuNav = function(menu){
37748     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37749     this.scope = this.menu = menu;
37750 };
37751
37752 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37753     doRelay : function(e, h){
37754         var k = e.getKey();
37755         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37756             this.menu.tryActivate(0, 1);
37757             return false;
37758         }
37759         return h.call(this.scope || this, e, this.menu);
37760     },
37761
37762     up : function(e, m){
37763         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37764             m.tryActivate(m.items.length-1, -1);
37765         }
37766     },
37767
37768     down : function(e, m){
37769         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37770             m.tryActivate(0, 1);
37771         }
37772     },
37773
37774     right : function(e, m){
37775         if(m.activeItem){
37776             m.activeItem.expandMenu(true);
37777         }
37778     },
37779
37780     left : function(e, m){
37781         m.hide();
37782         if(m.parentMenu && m.parentMenu.activeItem){
37783             m.parentMenu.activeItem.activate();
37784         }
37785     },
37786
37787     enter : function(e, m){
37788         if(m.activeItem){
37789             e.stopPropagation();
37790             m.activeItem.onClick(e);
37791             m.fireEvent("click", this, m.activeItem);
37792             return true;
37793         }
37794     }
37795 });/*
37796  * Based on:
37797  * Ext JS Library 1.1.1
37798  * Copyright(c) 2006-2007, Ext JS, LLC.
37799  *
37800  * Originally Released Under LGPL - original licence link has changed is not relivant.
37801  *
37802  * Fork - LGPL
37803  * <script type="text/javascript">
37804  */
37805  
37806 /**
37807  * @class Roo.menu.MenuMgr
37808  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37809  * @singleton
37810  */
37811 Roo.menu.MenuMgr = function(){
37812    var menus, active, groups = {}, attached = false, lastShow = new Date();
37813
37814    // private - called when first menu is created
37815    function init(){
37816        menus = {};
37817        active = new Roo.util.MixedCollection();
37818        Roo.get(document).addKeyListener(27, function(){
37819            if(active.length > 0){
37820                hideAll();
37821            }
37822        });
37823    }
37824
37825    // private
37826    function hideAll(){
37827        if(active && active.length > 0){
37828            var c = active.clone();
37829            c.each(function(m){
37830                m.hide();
37831            });
37832        }
37833    }
37834
37835    // private
37836    function onHide(m){
37837        active.remove(m);
37838        if(active.length < 1){
37839            Roo.get(document).un("mousedown", onMouseDown);
37840            attached = false;
37841        }
37842    }
37843
37844    // private
37845    function onShow(m){
37846        var last = active.last();
37847        lastShow = new Date();
37848        active.add(m);
37849        if(!attached){
37850            Roo.get(document).on("mousedown", onMouseDown);
37851            attached = true;
37852        }
37853        if(m.parentMenu){
37854           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37855           m.parentMenu.activeChild = m;
37856        }else if(last && last.isVisible()){
37857           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37858        }
37859    }
37860
37861    // private
37862    function onBeforeHide(m){
37863        if(m.activeChild){
37864            m.activeChild.hide();
37865        }
37866        if(m.autoHideTimer){
37867            clearTimeout(m.autoHideTimer);
37868            delete m.autoHideTimer;
37869        }
37870    }
37871
37872    // private
37873    function onBeforeShow(m){
37874        var pm = m.parentMenu;
37875        if(!pm && !m.allowOtherMenus){
37876            hideAll();
37877        }else if(pm && pm.activeChild && active != m){
37878            pm.activeChild.hide();
37879        }
37880    }
37881
37882    // private
37883    function onMouseDown(e){
37884        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37885            hideAll();
37886        }
37887    }
37888
37889    // private
37890    function onBeforeCheck(mi, state){
37891        if(state){
37892            var g = groups[mi.group];
37893            for(var i = 0, l = g.length; i < l; i++){
37894                if(g[i] != mi){
37895                    g[i].setChecked(false);
37896                }
37897            }
37898        }
37899    }
37900
37901    return {
37902
37903        /**
37904         * Hides all menus that are currently visible
37905         */
37906        hideAll : function(){
37907             hideAll();  
37908        },
37909
37910        // private
37911        register : function(menu){
37912            if(!menus){
37913                init();
37914            }
37915            menus[menu.id] = menu;
37916            menu.on("beforehide", onBeforeHide);
37917            menu.on("hide", onHide);
37918            menu.on("beforeshow", onBeforeShow);
37919            menu.on("show", onShow);
37920            var g = menu.group;
37921            if(g && menu.events["checkchange"]){
37922                if(!groups[g]){
37923                    groups[g] = [];
37924                }
37925                groups[g].push(menu);
37926                menu.on("checkchange", onCheck);
37927            }
37928        },
37929
37930         /**
37931          * Returns a {@link Roo.menu.Menu} object
37932          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37933          * be used to generate and return a new Menu instance.
37934          */
37935        get : function(menu){
37936            if(typeof menu == "string"){ // menu id
37937                return menus[menu];
37938            }else if(menu.events){  // menu instance
37939                return menu;
37940            }else if(typeof menu.length == 'number'){ // array of menu items?
37941                return new Roo.menu.Menu({items:menu});
37942            }else{ // otherwise, must be a config
37943                return new Roo.menu.Menu(menu);
37944            }
37945        },
37946
37947        // private
37948        unregister : function(menu){
37949            delete menus[menu.id];
37950            menu.un("beforehide", onBeforeHide);
37951            menu.un("hide", onHide);
37952            menu.un("beforeshow", onBeforeShow);
37953            menu.un("show", onShow);
37954            var g = menu.group;
37955            if(g && menu.events["checkchange"]){
37956                groups[g].remove(menu);
37957                menu.un("checkchange", onCheck);
37958            }
37959        },
37960
37961        // private
37962        registerCheckable : function(menuItem){
37963            var g = menuItem.group;
37964            if(g){
37965                if(!groups[g]){
37966                    groups[g] = [];
37967                }
37968                groups[g].push(menuItem);
37969                menuItem.on("beforecheckchange", onBeforeCheck);
37970            }
37971        },
37972
37973        // private
37974        unregisterCheckable : function(menuItem){
37975            var g = menuItem.group;
37976            if(g){
37977                groups[g].remove(menuItem);
37978                menuItem.un("beforecheckchange", onBeforeCheck);
37979            }
37980        }
37981    };
37982 }();/*
37983  * Based on:
37984  * Ext JS Library 1.1.1
37985  * Copyright(c) 2006-2007, Ext JS, LLC.
37986  *
37987  * Originally Released Under LGPL - original licence link has changed is not relivant.
37988  *
37989  * Fork - LGPL
37990  * <script type="text/javascript">
37991  */
37992  
37993
37994 /**
37995  * @class Roo.menu.BaseItem
37996  * @extends Roo.Component
37997  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37998  * management and base configuration options shared by all menu components.
37999  * @constructor
38000  * Creates a new BaseItem
38001  * @param {Object} config Configuration options
38002  */
38003 Roo.menu.BaseItem = function(config){
38004     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38005
38006     this.addEvents({
38007         /**
38008          * @event click
38009          * Fires when this item is clicked
38010          * @param {Roo.menu.BaseItem} this
38011          * @param {Roo.EventObject} e
38012          */
38013         click: true,
38014         /**
38015          * @event activate
38016          * Fires when this item is activated
38017          * @param {Roo.menu.BaseItem} this
38018          */
38019         activate : true,
38020         /**
38021          * @event deactivate
38022          * Fires when this item is deactivated
38023          * @param {Roo.menu.BaseItem} this
38024          */
38025         deactivate : true
38026     });
38027
38028     if(this.handler){
38029         this.on("click", this.handler, this.scope, true);
38030     }
38031 };
38032
38033 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38034     /**
38035      * @cfg {Function} handler
38036      * A function that will handle the click event of this menu item (defaults to undefined)
38037      */
38038     /**
38039      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38040      */
38041     canActivate : false,
38042     
38043      /**
38044      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38045      */
38046     hidden: false,
38047     
38048     /**
38049      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38050      */
38051     activeClass : "x-menu-item-active",
38052     /**
38053      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38054      */
38055     hideOnClick : true,
38056     /**
38057      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38058      */
38059     hideDelay : 100,
38060
38061     // private
38062     ctype: "Roo.menu.BaseItem",
38063
38064     // private
38065     actionMode : "container",
38066
38067     // private
38068     render : function(container, parentMenu){
38069         this.parentMenu = parentMenu;
38070         Roo.menu.BaseItem.superclass.render.call(this, container);
38071         this.container.menuItemId = this.id;
38072     },
38073
38074     // private
38075     onRender : function(container, position){
38076         this.el = Roo.get(this.el);
38077         container.dom.appendChild(this.el.dom);
38078     },
38079
38080     // private
38081     onClick : function(e){
38082         if(!this.disabled && this.fireEvent("click", this, e) !== false
38083                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38084             this.handleClick(e);
38085         }else{
38086             e.stopEvent();
38087         }
38088     },
38089
38090     // private
38091     activate : function(){
38092         if(this.disabled){
38093             return false;
38094         }
38095         var li = this.container;
38096         li.addClass(this.activeClass);
38097         this.region = li.getRegion().adjust(2, 2, -2, -2);
38098         this.fireEvent("activate", this);
38099         return true;
38100     },
38101
38102     // private
38103     deactivate : function(){
38104         this.container.removeClass(this.activeClass);
38105         this.fireEvent("deactivate", this);
38106     },
38107
38108     // private
38109     shouldDeactivate : function(e){
38110         return !this.region || !this.region.contains(e.getPoint());
38111     },
38112
38113     // private
38114     handleClick : function(e){
38115         if(this.hideOnClick){
38116             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38117         }
38118     },
38119
38120     // private
38121     expandMenu : function(autoActivate){
38122         // do nothing
38123     },
38124
38125     // private
38126     hideMenu : function(){
38127         // do nothing
38128     }
38129 });/*
38130  * Based on:
38131  * Ext JS Library 1.1.1
38132  * Copyright(c) 2006-2007, Ext JS, LLC.
38133  *
38134  * Originally Released Under LGPL - original licence link has changed is not relivant.
38135  *
38136  * Fork - LGPL
38137  * <script type="text/javascript">
38138  */
38139  
38140 /**
38141  * @class Roo.menu.Adapter
38142  * @extends Roo.menu.BaseItem
38143  * 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.
38144  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38145  * @constructor
38146  * Creates a new Adapter
38147  * @param {Object} config Configuration options
38148  */
38149 Roo.menu.Adapter = function(component, config){
38150     Roo.menu.Adapter.superclass.constructor.call(this, config);
38151     this.component = component;
38152 };
38153 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38154     // private
38155     canActivate : true,
38156
38157     // private
38158     onRender : function(container, position){
38159         this.component.render(container);
38160         this.el = this.component.getEl();
38161     },
38162
38163     // private
38164     activate : function(){
38165         if(this.disabled){
38166             return false;
38167         }
38168         this.component.focus();
38169         this.fireEvent("activate", this);
38170         return true;
38171     },
38172
38173     // private
38174     deactivate : function(){
38175         this.fireEvent("deactivate", this);
38176     },
38177
38178     // private
38179     disable : function(){
38180         this.component.disable();
38181         Roo.menu.Adapter.superclass.disable.call(this);
38182     },
38183
38184     // private
38185     enable : function(){
38186         this.component.enable();
38187         Roo.menu.Adapter.superclass.enable.call(this);
38188     }
38189 });/*
38190  * Based on:
38191  * Ext JS Library 1.1.1
38192  * Copyright(c) 2006-2007, Ext JS, LLC.
38193  *
38194  * Originally Released Under LGPL - original licence link has changed is not relivant.
38195  *
38196  * Fork - LGPL
38197  * <script type="text/javascript">
38198  */
38199
38200 /**
38201  * @class Roo.menu.TextItem
38202  * @extends Roo.menu.BaseItem
38203  * Adds a static text string to a menu, usually used as either a heading or group separator.
38204  * Note: old style constructor with text is still supported.
38205  * 
38206  * @constructor
38207  * Creates a new TextItem
38208  * @param {Object} cfg Configuration
38209  */
38210 Roo.menu.TextItem = function(cfg){
38211     if (typeof(cfg) == 'string') {
38212         this.text = cfg;
38213     } else {
38214         Roo.apply(this,cfg);
38215     }
38216     
38217     Roo.menu.TextItem.superclass.constructor.call(this);
38218 };
38219
38220 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38221     /**
38222      * @cfg {Boolean} text Text to show on item.
38223      */
38224     text : '',
38225     
38226     /**
38227      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38228      */
38229     hideOnClick : false,
38230     /**
38231      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38232      */
38233     itemCls : "x-menu-text",
38234
38235     // private
38236     onRender : function(){
38237         var s = document.createElement("span");
38238         s.className = this.itemCls;
38239         s.innerHTML = this.text;
38240         this.el = s;
38241         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38242     }
38243 });/*
38244  * Based on:
38245  * Ext JS Library 1.1.1
38246  * Copyright(c) 2006-2007, Ext JS, LLC.
38247  *
38248  * Originally Released Under LGPL - original licence link has changed is not relivant.
38249  *
38250  * Fork - LGPL
38251  * <script type="text/javascript">
38252  */
38253
38254 /**
38255  * @class Roo.menu.Separator
38256  * @extends Roo.menu.BaseItem
38257  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38258  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38259  * @constructor
38260  * @param {Object} config Configuration options
38261  */
38262 Roo.menu.Separator = function(config){
38263     Roo.menu.Separator.superclass.constructor.call(this, config);
38264 };
38265
38266 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38267     /**
38268      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38269      */
38270     itemCls : "x-menu-sep",
38271     /**
38272      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38273      */
38274     hideOnClick : false,
38275
38276     // private
38277     onRender : function(li){
38278         var s = document.createElement("span");
38279         s.className = this.itemCls;
38280         s.innerHTML = "&#160;";
38281         this.el = s;
38282         li.addClass("x-menu-sep-li");
38283         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38284     }
38285 });/*
38286  * Based on:
38287  * Ext JS Library 1.1.1
38288  * Copyright(c) 2006-2007, Ext JS, LLC.
38289  *
38290  * Originally Released Under LGPL - original licence link has changed is not relivant.
38291  *
38292  * Fork - LGPL
38293  * <script type="text/javascript">
38294  */
38295 /**
38296  * @class Roo.menu.Item
38297  * @extends Roo.menu.BaseItem
38298  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38299  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38300  * activation and click handling.
38301  * @constructor
38302  * Creates a new Item
38303  * @param {Object} config Configuration options
38304  */
38305 Roo.menu.Item = function(config){
38306     Roo.menu.Item.superclass.constructor.call(this, config);
38307     if(this.menu){
38308         this.menu = Roo.menu.MenuMgr.get(this.menu);
38309     }
38310 };
38311 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38312     
38313     /**
38314      * @cfg {String} text
38315      * The text to show on the menu item.
38316      */
38317     text: '',
38318      /**
38319      * @cfg {String} HTML to render in menu
38320      * The text to show on the menu item (HTML version).
38321      */
38322     html: '',
38323     /**
38324      * @cfg {String} icon
38325      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38326      */
38327     icon: undefined,
38328     /**
38329      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38330      */
38331     itemCls : "x-menu-item",
38332     /**
38333      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38334      */
38335     canActivate : true,
38336     /**
38337      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38338      */
38339     showDelay: 200,
38340     // doc'd in BaseItem
38341     hideDelay: 200,
38342
38343     // private
38344     ctype: "Roo.menu.Item",
38345     
38346     // private
38347     onRender : function(container, position){
38348         var el = document.createElement("a");
38349         el.hideFocus = true;
38350         el.unselectable = "on";
38351         el.href = this.href || "#";
38352         if(this.hrefTarget){
38353             el.target = this.hrefTarget;
38354         }
38355         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38356         
38357         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38358         
38359         el.innerHTML = String.format(
38360                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38361                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38362         this.el = el;
38363         Roo.menu.Item.superclass.onRender.call(this, container, position);
38364     },
38365
38366     /**
38367      * Sets the text to display in this menu item
38368      * @param {String} text The text to display
38369      * @param {Boolean} isHTML true to indicate text is pure html.
38370      */
38371     setText : function(text, isHTML){
38372         if (isHTML) {
38373             this.html = text;
38374         } else {
38375             this.text = text;
38376             this.html = '';
38377         }
38378         if(this.rendered){
38379             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38380      
38381             this.el.update(String.format(
38382                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38383                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38384             this.parentMenu.autoWidth();
38385         }
38386     },
38387
38388     // private
38389     handleClick : function(e){
38390         if(!this.href){ // if no link defined, stop the event automatically
38391             e.stopEvent();
38392         }
38393         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38394     },
38395
38396     // private
38397     activate : function(autoExpand){
38398         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38399             this.focus();
38400             if(autoExpand){
38401                 this.expandMenu();
38402             }
38403         }
38404         return true;
38405     },
38406
38407     // private
38408     shouldDeactivate : function(e){
38409         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38410             if(this.menu && this.menu.isVisible()){
38411                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38412             }
38413             return true;
38414         }
38415         return false;
38416     },
38417
38418     // private
38419     deactivate : function(){
38420         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38421         this.hideMenu();
38422     },
38423
38424     // private
38425     expandMenu : function(autoActivate){
38426         if(!this.disabled && this.menu){
38427             clearTimeout(this.hideTimer);
38428             delete this.hideTimer;
38429             if(!this.menu.isVisible() && !this.showTimer){
38430                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38431             }else if (this.menu.isVisible() && autoActivate){
38432                 this.menu.tryActivate(0, 1);
38433             }
38434         }
38435     },
38436
38437     // private
38438     deferExpand : function(autoActivate){
38439         delete this.showTimer;
38440         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38441         if(autoActivate){
38442             this.menu.tryActivate(0, 1);
38443         }
38444     },
38445
38446     // private
38447     hideMenu : function(){
38448         clearTimeout(this.showTimer);
38449         delete this.showTimer;
38450         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38451             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38452         }
38453     },
38454
38455     // private
38456     deferHide : function(){
38457         delete this.hideTimer;
38458         this.menu.hide();
38459     }
38460 });/*
38461  * Based on:
38462  * Ext JS Library 1.1.1
38463  * Copyright(c) 2006-2007, Ext JS, LLC.
38464  *
38465  * Originally Released Under LGPL - original licence link has changed is not relivant.
38466  *
38467  * Fork - LGPL
38468  * <script type="text/javascript">
38469  */
38470  
38471 /**
38472  * @class Roo.menu.CheckItem
38473  * @extends Roo.menu.Item
38474  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38475  * @constructor
38476  * Creates a new CheckItem
38477  * @param {Object} config Configuration options
38478  */
38479 Roo.menu.CheckItem = function(config){
38480     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38481     this.addEvents({
38482         /**
38483          * @event beforecheckchange
38484          * Fires before the checked value is set, providing an opportunity to cancel if needed
38485          * @param {Roo.menu.CheckItem} this
38486          * @param {Boolean} checked The new checked value that will be set
38487          */
38488         "beforecheckchange" : true,
38489         /**
38490          * @event checkchange
38491          * Fires after the checked value has been set
38492          * @param {Roo.menu.CheckItem} this
38493          * @param {Boolean} checked The checked value that was set
38494          */
38495         "checkchange" : true
38496     });
38497     if(this.checkHandler){
38498         this.on('checkchange', this.checkHandler, this.scope);
38499     }
38500 };
38501 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38502     /**
38503      * @cfg {String} group
38504      * All check items with the same group name will automatically be grouped into a single-select
38505      * radio button group (defaults to '')
38506      */
38507     /**
38508      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38509      */
38510     itemCls : "x-menu-item x-menu-check-item",
38511     /**
38512      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38513      */
38514     groupClass : "x-menu-group-item",
38515
38516     /**
38517      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38518      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38519      * initialized with checked = true will be rendered as checked.
38520      */
38521     checked: false,
38522
38523     // private
38524     ctype: "Roo.menu.CheckItem",
38525
38526     // private
38527     onRender : function(c){
38528         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38529         if(this.group){
38530             this.el.addClass(this.groupClass);
38531         }
38532         Roo.menu.MenuMgr.registerCheckable(this);
38533         if(this.checked){
38534             this.checked = false;
38535             this.setChecked(true, true);
38536         }
38537     },
38538
38539     // private
38540     destroy : function(){
38541         if(this.rendered){
38542             Roo.menu.MenuMgr.unregisterCheckable(this);
38543         }
38544         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38545     },
38546
38547     /**
38548      * Set the checked state of this item
38549      * @param {Boolean} checked The new checked value
38550      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38551      */
38552     setChecked : function(state, suppressEvent){
38553         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38554             if(this.container){
38555                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38556             }
38557             this.checked = state;
38558             if(suppressEvent !== true){
38559                 this.fireEvent("checkchange", this, state);
38560             }
38561         }
38562     },
38563
38564     // private
38565     handleClick : function(e){
38566        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38567            this.setChecked(!this.checked);
38568        }
38569        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38570     }
38571 });/*
38572  * Based on:
38573  * Ext JS Library 1.1.1
38574  * Copyright(c) 2006-2007, Ext JS, LLC.
38575  *
38576  * Originally Released Under LGPL - original licence link has changed is not relivant.
38577  *
38578  * Fork - LGPL
38579  * <script type="text/javascript">
38580  */
38581  
38582 /**
38583  * @class Roo.menu.DateItem
38584  * @extends Roo.menu.Adapter
38585  * A menu item that wraps the {@link Roo.DatPicker} component.
38586  * @constructor
38587  * Creates a new DateItem
38588  * @param {Object} config Configuration options
38589  */
38590 Roo.menu.DateItem = function(config){
38591     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38592     /** The Roo.DatePicker object @type Roo.DatePicker */
38593     this.picker = this.component;
38594     this.addEvents({select: true});
38595     
38596     this.picker.on("render", function(picker){
38597         picker.getEl().swallowEvent("click");
38598         picker.container.addClass("x-menu-date-item");
38599     });
38600
38601     this.picker.on("select", this.onSelect, this);
38602 };
38603
38604 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38605     // private
38606     onSelect : function(picker, date){
38607         this.fireEvent("select", this, date, picker);
38608         Roo.menu.DateItem.superclass.handleClick.call(this);
38609     }
38610 });/*
38611  * Based on:
38612  * Ext JS Library 1.1.1
38613  * Copyright(c) 2006-2007, Ext JS, LLC.
38614  *
38615  * Originally Released Under LGPL - original licence link has changed is not relivant.
38616  *
38617  * Fork - LGPL
38618  * <script type="text/javascript">
38619  */
38620  
38621 /**
38622  * @class Roo.menu.ColorItem
38623  * @extends Roo.menu.Adapter
38624  * A menu item that wraps the {@link Roo.ColorPalette} component.
38625  * @constructor
38626  * Creates a new ColorItem
38627  * @param {Object} config Configuration options
38628  */
38629 Roo.menu.ColorItem = function(config){
38630     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38631     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38632     this.palette = this.component;
38633     this.relayEvents(this.palette, ["select"]);
38634     if(this.selectHandler){
38635         this.on('select', this.selectHandler, this.scope);
38636     }
38637 };
38638 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38639  * Based on:
38640  * Ext JS Library 1.1.1
38641  * Copyright(c) 2006-2007, Ext JS, LLC.
38642  *
38643  * Originally Released Under LGPL - original licence link has changed is not relivant.
38644  *
38645  * Fork - LGPL
38646  * <script type="text/javascript">
38647  */
38648  
38649
38650 /**
38651  * @class Roo.menu.DateMenu
38652  * @extends Roo.menu.Menu
38653  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38654  * @constructor
38655  * Creates a new DateMenu
38656  * @param {Object} config Configuration options
38657  */
38658 Roo.menu.DateMenu = function(config){
38659     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38660     this.plain = true;
38661     var di = new Roo.menu.DateItem(config);
38662     this.add(di);
38663     /**
38664      * The {@link Roo.DatePicker} instance for this DateMenu
38665      * @type DatePicker
38666      */
38667     this.picker = di.picker;
38668     /**
38669      * @event select
38670      * @param {DatePicker} picker
38671      * @param {Date} date
38672      */
38673     this.relayEvents(di, ["select"]);
38674     this.on('beforeshow', function(){
38675         if(this.picker){
38676             this.picker.hideMonthPicker(false);
38677         }
38678     }, this);
38679 };
38680 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38681     cls:'x-date-menu'
38682 });/*
38683  * Based on:
38684  * Ext JS Library 1.1.1
38685  * Copyright(c) 2006-2007, Ext JS, LLC.
38686  *
38687  * Originally Released Under LGPL - original licence link has changed is not relivant.
38688  *
38689  * Fork - LGPL
38690  * <script type="text/javascript">
38691  */
38692  
38693
38694 /**
38695  * @class Roo.menu.ColorMenu
38696  * @extends Roo.menu.Menu
38697  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38698  * @constructor
38699  * Creates a new ColorMenu
38700  * @param {Object} config Configuration options
38701  */
38702 Roo.menu.ColorMenu = function(config){
38703     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38704     this.plain = true;
38705     var ci = new Roo.menu.ColorItem(config);
38706     this.add(ci);
38707     /**
38708      * The {@link Roo.ColorPalette} instance for this ColorMenu
38709      * @type ColorPalette
38710      */
38711     this.palette = ci.palette;
38712     /**
38713      * @event select
38714      * @param {ColorPalette} palette
38715      * @param {String} color
38716      */
38717     this.relayEvents(ci, ["select"]);
38718 };
38719 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38720  * Based on:
38721  * Ext JS Library 1.1.1
38722  * Copyright(c) 2006-2007, Ext JS, LLC.
38723  *
38724  * Originally Released Under LGPL - original licence link has changed is not relivant.
38725  *
38726  * Fork - LGPL
38727  * <script type="text/javascript">
38728  */
38729  
38730 /**
38731  * @class Roo.form.TextItem
38732  * @extends Roo.BoxComponent
38733  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38734  * @constructor
38735  * Creates a new TextItem
38736  * @param {Object} config Configuration options
38737  */
38738 Roo.form.TextItem = function(config){
38739     Roo.form.TextItem.superclass.constructor.call(this, config);
38740 };
38741
38742 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38743     
38744     /**
38745      * @cfg {String} tag the tag for this item (default div)
38746      */
38747     tag : 'div',
38748     /**
38749      * @cfg {String} html the content for this item
38750      */
38751     html : '',
38752     
38753     getAutoCreate : function()
38754     {
38755         var cfg = {
38756             id: this.id,
38757             tag: this.tag,
38758             html: this.html,
38759             cls: 'x-form-item'
38760         };
38761         
38762         return cfg;
38763         
38764     },
38765     
38766     onRender : function(ct, position)
38767     {
38768         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38769         
38770         if(!this.el){
38771             var cfg = this.getAutoCreate();
38772             if(!cfg.name){
38773                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38774             }
38775             if (!cfg.name.length) {
38776                 delete cfg.name;
38777             }
38778             this.el = ct.createChild(cfg, position);
38779         }
38780     }
38781     
38782 });/*
38783  * Based on:
38784  * Ext JS Library 1.1.1
38785  * Copyright(c) 2006-2007, Ext JS, LLC.
38786  *
38787  * Originally Released Under LGPL - original licence link has changed is not relivant.
38788  *
38789  * Fork - LGPL
38790  * <script type="text/javascript">
38791  */
38792  
38793 /**
38794  * @class Roo.form.Field
38795  * @extends Roo.BoxComponent
38796  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38797  * @constructor
38798  * Creates a new Field
38799  * @param {Object} config Configuration options
38800  */
38801 Roo.form.Field = function(config){
38802     Roo.form.Field.superclass.constructor.call(this, config);
38803 };
38804
38805 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38806     /**
38807      * @cfg {String} fieldLabel Label to use when rendering a form.
38808      */
38809        /**
38810      * @cfg {String} qtip Mouse over tip
38811      */
38812      
38813     /**
38814      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38815      */
38816     invalidClass : "x-form-invalid",
38817     /**
38818      * @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")
38819      */
38820     invalidText : "The value in this field is invalid",
38821     /**
38822      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38823      */
38824     focusClass : "x-form-focus",
38825     /**
38826      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38827       automatic validation (defaults to "keyup").
38828      */
38829     validationEvent : "keyup",
38830     /**
38831      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38832      */
38833     validateOnBlur : true,
38834     /**
38835      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38836      */
38837     validationDelay : 250,
38838     /**
38839      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38840      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38841      */
38842     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38843     /**
38844      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38845      */
38846     fieldClass : "x-form-field",
38847     /**
38848      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38849      *<pre>
38850 Value         Description
38851 -----------   ----------------------------------------------------------------------
38852 qtip          Display a quick tip when the user hovers over the field
38853 title         Display a default browser title attribute popup
38854 under         Add a block div beneath the field containing the error text
38855 side          Add an error icon to the right of the field with a popup on hover
38856 [element id]  Add the error text directly to the innerHTML of the specified element
38857 </pre>
38858      */
38859     msgTarget : 'qtip',
38860     /**
38861      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38862      */
38863     msgFx : 'normal',
38864
38865     /**
38866      * @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.
38867      */
38868     readOnly : false,
38869
38870     /**
38871      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38872      */
38873     disabled : false,
38874
38875     /**
38876      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38877      */
38878     inputType : undefined,
38879     
38880     /**
38881      * @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).
38882          */
38883         tabIndex : undefined,
38884         
38885     // private
38886     isFormField : true,
38887
38888     // private
38889     hasFocus : false,
38890     /**
38891      * @property {Roo.Element} fieldEl
38892      * Element Containing the rendered Field (with label etc.)
38893      */
38894     /**
38895      * @cfg {Mixed} value A value to initialize this field with.
38896      */
38897     value : undefined,
38898
38899     /**
38900      * @cfg {String} name The field's HTML name attribute.
38901      */
38902     /**
38903      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38904      */
38905     // private
38906     loadedValue : false,
38907      
38908      
38909         // private ??
38910         initComponent : function(){
38911         Roo.form.Field.superclass.initComponent.call(this);
38912         this.addEvents({
38913             /**
38914              * @event focus
38915              * Fires when this field receives input focus.
38916              * @param {Roo.form.Field} this
38917              */
38918             focus : true,
38919             /**
38920              * @event blur
38921              * Fires when this field loses input focus.
38922              * @param {Roo.form.Field} this
38923              */
38924             blur : true,
38925             /**
38926              * @event specialkey
38927              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38928              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38929              * @param {Roo.form.Field} this
38930              * @param {Roo.EventObject} e The event object
38931              */
38932             specialkey : true,
38933             /**
38934              * @event change
38935              * Fires just before the field blurs if the field value has changed.
38936              * @param {Roo.form.Field} this
38937              * @param {Mixed} newValue The new value
38938              * @param {Mixed} oldValue The original value
38939              */
38940             change : true,
38941             /**
38942              * @event invalid
38943              * Fires after the field has been marked as invalid.
38944              * @param {Roo.form.Field} this
38945              * @param {String} msg The validation message
38946              */
38947             invalid : true,
38948             /**
38949              * @event valid
38950              * Fires after the field has been validated with no errors.
38951              * @param {Roo.form.Field} this
38952              */
38953             valid : true,
38954              /**
38955              * @event keyup
38956              * Fires after the key up
38957              * @param {Roo.form.Field} this
38958              * @param {Roo.EventObject}  e The event Object
38959              */
38960             keyup : true
38961         });
38962     },
38963
38964     /**
38965      * Returns the name attribute of the field if available
38966      * @return {String} name The field name
38967      */
38968     getName: function(){
38969          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38970     },
38971
38972     // private
38973     onRender : function(ct, position){
38974         Roo.form.Field.superclass.onRender.call(this, ct, position);
38975         if(!this.el){
38976             var cfg = this.getAutoCreate();
38977             if(!cfg.name){
38978                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38979             }
38980             if (!cfg.name.length) {
38981                 delete cfg.name;
38982             }
38983             if(this.inputType){
38984                 cfg.type = this.inputType;
38985             }
38986             this.el = ct.createChild(cfg, position);
38987         }
38988         var type = this.el.dom.type;
38989         if(type){
38990             if(type == 'password'){
38991                 type = 'text';
38992             }
38993             this.el.addClass('x-form-'+type);
38994         }
38995         if(this.readOnly){
38996             this.el.dom.readOnly = true;
38997         }
38998         if(this.tabIndex !== undefined){
38999             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39000         }
39001
39002         this.el.addClass([this.fieldClass, this.cls]);
39003         this.initValue();
39004     },
39005
39006     /**
39007      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39008      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39009      * @return {Roo.form.Field} this
39010      */
39011     applyTo : function(target){
39012         this.allowDomMove = false;
39013         this.el = Roo.get(target);
39014         this.render(this.el.dom.parentNode);
39015         return this;
39016     },
39017
39018     // private
39019     initValue : function(){
39020         if(this.value !== undefined){
39021             this.setValue(this.value);
39022         }else if(this.el.dom.value.length > 0){
39023             this.setValue(this.el.dom.value);
39024         }
39025     },
39026
39027     /**
39028      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39029      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39030      */
39031     isDirty : function() {
39032         if(this.disabled) {
39033             return false;
39034         }
39035         return String(this.getValue()) !== String(this.originalValue);
39036     },
39037
39038     /**
39039      * stores the current value in loadedValue
39040      */
39041     resetHasChanged : function()
39042     {
39043         this.loadedValue = String(this.getValue());
39044     },
39045     /**
39046      * checks the current value against the 'loaded' value.
39047      * Note - will return false if 'resetHasChanged' has not been called first.
39048      */
39049     hasChanged : function()
39050     {
39051         if(this.disabled || this.readOnly) {
39052             return false;
39053         }
39054         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39055     },
39056     
39057     
39058     
39059     // private
39060     afterRender : function(){
39061         Roo.form.Field.superclass.afterRender.call(this);
39062         this.initEvents();
39063     },
39064
39065     // private
39066     fireKey : function(e){
39067         //Roo.log('field ' + e.getKey());
39068         if(e.isNavKeyPress()){
39069             this.fireEvent("specialkey", this, e);
39070         }
39071     },
39072
39073     /**
39074      * Resets the current field value to the originally loaded value and clears any validation messages
39075      */
39076     reset : function(){
39077         this.setValue(this.resetValue);
39078         this.originalValue = this.getValue();
39079         this.clearInvalid();
39080     },
39081
39082     // private
39083     initEvents : function(){
39084         // safari killled keypress - so keydown is now used..
39085         this.el.on("keydown" , this.fireKey,  this);
39086         this.el.on("focus", this.onFocus,  this);
39087         this.el.on("blur", this.onBlur,  this);
39088         this.el.relayEvent('keyup', this);
39089
39090         // reference to original value for reset
39091         this.originalValue = this.getValue();
39092         this.resetValue =  this.getValue();
39093     },
39094
39095     // private
39096     onFocus : function(){
39097         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39098             this.el.addClass(this.focusClass);
39099         }
39100         if(!this.hasFocus){
39101             this.hasFocus = true;
39102             this.startValue = this.getValue();
39103             this.fireEvent("focus", this);
39104         }
39105     },
39106
39107     beforeBlur : Roo.emptyFn,
39108
39109     // private
39110     onBlur : function(){
39111         this.beforeBlur();
39112         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39113             this.el.removeClass(this.focusClass);
39114         }
39115         this.hasFocus = false;
39116         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39117             this.validate();
39118         }
39119         var v = this.getValue();
39120         if(String(v) !== String(this.startValue)){
39121             this.fireEvent('change', this, v, this.startValue);
39122         }
39123         this.fireEvent("blur", this);
39124     },
39125
39126     /**
39127      * Returns whether or not the field value is currently valid
39128      * @param {Boolean} preventMark True to disable marking the field invalid
39129      * @return {Boolean} True if the value is valid, else false
39130      */
39131     isValid : function(preventMark){
39132         if(this.disabled){
39133             return true;
39134         }
39135         var restore = this.preventMark;
39136         this.preventMark = preventMark === true;
39137         var v = this.validateValue(this.processValue(this.getRawValue()));
39138         this.preventMark = restore;
39139         return v;
39140     },
39141
39142     /**
39143      * Validates the field value
39144      * @return {Boolean} True if the value is valid, else false
39145      */
39146     validate : function(){
39147         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39148             this.clearInvalid();
39149             return true;
39150         }
39151         return false;
39152     },
39153
39154     processValue : function(value){
39155         return value;
39156     },
39157
39158     // private
39159     // Subclasses should provide the validation implementation by overriding this
39160     validateValue : function(value){
39161         return true;
39162     },
39163
39164     /**
39165      * Mark this field as invalid
39166      * @param {String} msg The validation message
39167      */
39168     markInvalid : function(msg){
39169         if(!this.rendered || this.preventMark){ // not rendered
39170             return;
39171         }
39172         
39173         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39174         
39175         obj.el.addClass(this.invalidClass);
39176         msg = msg || this.invalidText;
39177         switch(this.msgTarget){
39178             case 'qtip':
39179                 obj.el.dom.qtip = msg;
39180                 obj.el.dom.qclass = 'x-form-invalid-tip';
39181                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39182                     Roo.QuickTips.enable();
39183                 }
39184                 break;
39185             case 'title':
39186                 this.el.dom.title = msg;
39187                 break;
39188             case 'under':
39189                 if(!this.errorEl){
39190                     var elp = this.el.findParent('.x-form-element', 5, true);
39191                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39192                     this.errorEl.setWidth(elp.getWidth(true)-20);
39193                 }
39194                 this.errorEl.update(msg);
39195                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39196                 break;
39197             case 'side':
39198                 if(!this.errorIcon){
39199                     var elp = this.el.findParent('.x-form-element', 5, true);
39200                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39201                 }
39202                 this.alignErrorIcon();
39203                 this.errorIcon.dom.qtip = msg;
39204                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39205                 this.errorIcon.show();
39206                 this.on('resize', this.alignErrorIcon, this);
39207                 break;
39208             default:
39209                 var t = Roo.getDom(this.msgTarget);
39210                 t.innerHTML = msg;
39211                 t.style.display = this.msgDisplay;
39212                 break;
39213         }
39214         this.fireEvent('invalid', this, msg);
39215     },
39216
39217     // private
39218     alignErrorIcon : function(){
39219         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39220     },
39221
39222     /**
39223      * Clear any invalid styles/messages for this field
39224      */
39225     clearInvalid : function(){
39226         if(!this.rendered || this.preventMark){ // not rendered
39227             return;
39228         }
39229         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39230         
39231         obj.el.removeClass(this.invalidClass);
39232         switch(this.msgTarget){
39233             case 'qtip':
39234                 obj.el.dom.qtip = '';
39235                 break;
39236             case 'title':
39237                 this.el.dom.title = '';
39238                 break;
39239             case 'under':
39240                 if(this.errorEl){
39241                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39242                 }
39243                 break;
39244             case 'side':
39245                 if(this.errorIcon){
39246                     this.errorIcon.dom.qtip = '';
39247                     this.errorIcon.hide();
39248                     this.un('resize', this.alignErrorIcon, this);
39249                 }
39250                 break;
39251             default:
39252                 var t = Roo.getDom(this.msgTarget);
39253                 t.innerHTML = '';
39254                 t.style.display = 'none';
39255                 break;
39256         }
39257         this.fireEvent('valid', this);
39258     },
39259
39260     /**
39261      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39262      * @return {Mixed} value The field value
39263      */
39264     getRawValue : function(){
39265         var v = this.el.getValue();
39266         
39267         return v;
39268     },
39269
39270     /**
39271      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39272      * @return {Mixed} value The field value
39273      */
39274     getValue : function(){
39275         var v = this.el.getValue();
39276          
39277         return v;
39278     },
39279
39280     /**
39281      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39282      * @param {Mixed} value The value to set
39283      */
39284     setRawValue : function(v){
39285         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39286     },
39287
39288     /**
39289      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39290      * @param {Mixed} value The value to set
39291      */
39292     setValue : function(v){
39293         this.value = v;
39294         if(this.rendered){
39295             this.el.dom.value = (v === null || v === undefined ? '' : v);
39296              this.validate();
39297         }
39298     },
39299
39300     adjustSize : function(w, h){
39301         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39302         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39303         return s;
39304     },
39305
39306     adjustWidth : function(tag, w){
39307         tag = tag.toLowerCase();
39308         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39309             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39310                 if(tag == 'input'){
39311                     return w + 2;
39312                 }
39313                 if(tag == 'textarea'){
39314                     return w-2;
39315                 }
39316             }else if(Roo.isOpera){
39317                 if(tag == 'input'){
39318                     return w + 2;
39319                 }
39320                 if(tag == 'textarea'){
39321                     return w-2;
39322                 }
39323             }
39324         }
39325         return w;
39326     }
39327 });
39328
39329
39330 // anything other than normal should be considered experimental
39331 Roo.form.Field.msgFx = {
39332     normal : {
39333         show: function(msgEl, f){
39334             msgEl.setDisplayed('block');
39335         },
39336
39337         hide : function(msgEl, f){
39338             msgEl.setDisplayed(false).update('');
39339         }
39340     },
39341
39342     slide : {
39343         show: function(msgEl, f){
39344             msgEl.slideIn('t', {stopFx:true});
39345         },
39346
39347         hide : function(msgEl, f){
39348             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39349         }
39350     },
39351
39352     slideRight : {
39353         show: function(msgEl, f){
39354             msgEl.fixDisplay();
39355             msgEl.alignTo(f.el, 'tl-tr');
39356             msgEl.slideIn('l', {stopFx:true});
39357         },
39358
39359         hide : function(msgEl, f){
39360             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39361         }
39362     }
39363 };/*
39364  * Based on:
39365  * Ext JS Library 1.1.1
39366  * Copyright(c) 2006-2007, Ext JS, LLC.
39367  *
39368  * Originally Released Under LGPL - original licence link has changed is not relivant.
39369  *
39370  * Fork - LGPL
39371  * <script type="text/javascript">
39372  */
39373  
39374
39375 /**
39376  * @class Roo.form.TextField
39377  * @extends Roo.form.Field
39378  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39379  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39380  * @constructor
39381  * Creates a new TextField
39382  * @param {Object} config Configuration options
39383  */
39384 Roo.form.TextField = function(config){
39385     Roo.form.TextField.superclass.constructor.call(this, config);
39386     this.addEvents({
39387         /**
39388          * @event autosize
39389          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39390          * according to the default logic, but this event provides a hook for the developer to apply additional
39391          * logic at runtime to resize the field if needed.
39392              * @param {Roo.form.Field} this This text field
39393              * @param {Number} width The new field width
39394              */
39395         autosize : true
39396     });
39397 };
39398
39399 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39400     /**
39401      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39402      */
39403     grow : false,
39404     /**
39405      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39406      */
39407     growMin : 30,
39408     /**
39409      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39410      */
39411     growMax : 800,
39412     /**
39413      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39414      */
39415     vtype : null,
39416     /**
39417      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39418      */
39419     maskRe : null,
39420     /**
39421      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39422      */
39423     disableKeyFilter : false,
39424     /**
39425      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39426      */
39427     allowBlank : true,
39428     /**
39429      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39430      */
39431     minLength : 0,
39432     /**
39433      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39434      */
39435     maxLength : Number.MAX_VALUE,
39436     /**
39437      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39438      */
39439     minLengthText : "The minimum length for this field is {0}",
39440     /**
39441      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39442      */
39443     maxLengthText : "The maximum length for this field is {0}",
39444     /**
39445      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39446      */
39447     selectOnFocus : false,
39448     /**
39449      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39450      */    
39451     allowLeadingSpace : false,
39452     /**
39453      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39454      */
39455     blankText : "This field is required",
39456     /**
39457      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39458      * If available, this function will be called only after the basic validators all return true, and will be passed the
39459      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39460      */
39461     validator : null,
39462     /**
39463      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39464      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39465      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39466      */
39467     regex : null,
39468     /**
39469      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39470      */
39471     regexText : "",
39472     /**
39473      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39474      */
39475     emptyText : null,
39476    
39477
39478     // private
39479     initEvents : function()
39480     {
39481         if (this.emptyText) {
39482             this.el.attr('placeholder', this.emptyText);
39483         }
39484         
39485         Roo.form.TextField.superclass.initEvents.call(this);
39486         if(this.validationEvent == 'keyup'){
39487             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39488             this.el.on('keyup', this.filterValidation, this);
39489         }
39490         else if(this.validationEvent !== false){
39491             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39492         }
39493         
39494         if(this.selectOnFocus){
39495             this.on("focus", this.preFocus, this);
39496         }
39497         if (!this.allowLeadingSpace) {
39498             this.on('blur', this.cleanLeadingSpace, this);
39499         }
39500         
39501         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39502             this.el.on("keypress", this.filterKeys, this);
39503         }
39504         if(this.grow){
39505             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39506             this.el.on("click", this.autoSize,  this);
39507         }
39508         if(this.el.is('input[type=password]') && Roo.isSafari){
39509             this.el.on('keydown', this.SafariOnKeyDown, this);
39510         }
39511     },
39512
39513     processValue : function(value){
39514         if(this.stripCharsRe){
39515             var newValue = value.replace(this.stripCharsRe, '');
39516             if(newValue !== value){
39517                 this.setRawValue(newValue);
39518                 return newValue;
39519             }
39520         }
39521         return value;
39522     },
39523
39524     filterValidation : function(e){
39525         if(!e.isNavKeyPress()){
39526             this.validationTask.delay(this.validationDelay);
39527         }
39528     },
39529
39530     // private
39531     onKeyUp : function(e){
39532         if(!e.isNavKeyPress()){
39533             this.autoSize();
39534         }
39535     },
39536     // private - clean the leading white space
39537     cleanLeadingSpace : function(e)
39538     {
39539         if ( this.inputType == 'file') {
39540             return;
39541         }
39542         
39543         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39544     },
39545     /**
39546      * Resets the current field value to the originally-loaded value and clears any validation messages.
39547      *  
39548      */
39549     reset : function(){
39550         Roo.form.TextField.superclass.reset.call(this);
39551        
39552     }, 
39553     // private
39554     preFocus : function(){
39555         
39556         if(this.selectOnFocus){
39557             this.el.dom.select();
39558         }
39559     },
39560
39561     
39562     // private
39563     filterKeys : function(e){
39564         var k = e.getKey();
39565         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39566             return;
39567         }
39568         var c = e.getCharCode(), cc = String.fromCharCode(c);
39569         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39570             return;
39571         }
39572         if(!this.maskRe.test(cc)){
39573             e.stopEvent();
39574         }
39575     },
39576
39577     setValue : function(v){
39578         
39579         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39580         
39581         this.autoSize();
39582     },
39583
39584     /**
39585      * Validates a value according to the field's validation rules and marks the field as invalid
39586      * if the validation fails
39587      * @param {Mixed} value The value to validate
39588      * @return {Boolean} True if the value is valid, else false
39589      */
39590     validateValue : function(value){
39591         if(value.length < 1)  { // if it's blank
39592              if(this.allowBlank){
39593                 this.clearInvalid();
39594                 return true;
39595              }else{
39596                 this.markInvalid(this.blankText);
39597                 return false;
39598              }
39599         }
39600         if(value.length < this.minLength){
39601             this.markInvalid(String.format(this.minLengthText, this.minLength));
39602             return false;
39603         }
39604         if(value.length > this.maxLength){
39605             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39606             return false;
39607         }
39608         if(this.vtype){
39609             var vt = Roo.form.VTypes;
39610             if(!vt[this.vtype](value, this)){
39611                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39612                 return false;
39613             }
39614         }
39615         if(typeof this.validator == "function"){
39616             var msg = this.validator(value);
39617             if(msg !== true){
39618                 this.markInvalid(msg);
39619                 return false;
39620             }
39621         }
39622         if(this.regex && !this.regex.test(value)){
39623             this.markInvalid(this.regexText);
39624             return false;
39625         }
39626         return true;
39627     },
39628
39629     /**
39630      * Selects text in this field
39631      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39632      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39633      */
39634     selectText : function(start, end){
39635         var v = this.getRawValue();
39636         if(v.length > 0){
39637             start = start === undefined ? 0 : start;
39638             end = end === undefined ? v.length : end;
39639             var d = this.el.dom;
39640             if(d.setSelectionRange){
39641                 d.setSelectionRange(start, end);
39642             }else if(d.createTextRange){
39643                 var range = d.createTextRange();
39644                 range.moveStart("character", start);
39645                 range.moveEnd("character", v.length-end);
39646                 range.select();
39647             }
39648         }
39649     },
39650
39651     /**
39652      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39653      * This only takes effect if grow = true, and fires the autosize event.
39654      */
39655     autoSize : function(){
39656         if(!this.grow || !this.rendered){
39657             return;
39658         }
39659         if(!this.metrics){
39660             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39661         }
39662         var el = this.el;
39663         var v = el.dom.value;
39664         var d = document.createElement('div');
39665         d.appendChild(document.createTextNode(v));
39666         v = d.innerHTML;
39667         d = null;
39668         v += "&#160;";
39669         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39670         this.el.setWidth(w);
39671         this.fireEvent("autosize", this, w);
39672     },
39673     
39674     // private
39675     SafariOnKeyDown : function(event)
39676     {
39677         // this is a workaround for a password hang bug on chrome/ webkit.
39678         
39679         var isSelectAll = false;
39680         
39681         if(this.el.dom.selectionEnd > 0){
39682             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39683         }
39684         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39685             event.preventDefault();
39686             this.setValue('');
39687             return;
39688         }
39689         
39690         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39691             
39692             event.preventDefault();
39693             // this is very hacky as keydown always get's upper case.
39694             
39695             var cc = String.fromCharCode(event.getCharCode());
39696             
39697             
39698             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39699             
39700         }
39701         
39702         
39703     }
39704 });/*
39705  * Based on:
39706  * Ext JS Library 1.1.1
39707  * Copyright(c) 2006-2007, Ext JS, LLC.
39708  *
39709  * Originally Released Under LGPL - original licence link has changed is not relivant.
39710  *
39711  * Fork - LGPL
39712  * <script type="text/javascript">
39713  */
39714  
39715 /**
39716  * @class Roo.form.Hidden
39717  * @extends Roo.form.TextField
39718  * Simple Hidden element used on forms 
39719  * 
39720  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39721  * 
39722  * @constructor
39723  * Creates a new Hidden form element.
39724  * @param {Object} config Configuration options
39725  */
39726
39727
39728
39729 // easy hidden field...
39730 Roo.form.Hidden = function(config){
39731     Roo.form.Hidden.superclass.constructor.call(this, config);
39732 };
39733   
39734 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39735     fieldLabel:      '',
39736     inputType:      'hidden',
39737     width:          50,
39738     allowBlank:     true,
39739     labelSeparator: '',
39740     hidden:         true,
39741     itemCls :       'x-form-item-display-none'
39742
39743
39744 });
39745
39746
39747 /*
39748  * Based on:
39749  * Ext JS Library 1.1.1
39750  * Copyright(c) 2006-2007, Ext JS, LLC.
39751  *
39752  * Originally Released Under LGPL - original licence link has changed is not relivant.
39753  *
39754  * Fork - LGPL
39755  * <script type="text/javascript">
39756  */
39757  
39758 /**
39759  * @class Roo.form.TriggerField
39760  * @extends Roo.form.TextField
39761  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39762  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39763  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39764  * for which you can provide a custom implementation.  For example:
39765  * <pre><code>
39766 var trigger = new Roo.form.TriggerField();
39767 trigger.onTriggerClick = myTriggerFn;
39768 trigger.applyTo('my-field');
39769 </code></pre>
39770  *
39771  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39772  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39773  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39774  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39775  * @constructor
39776  * Create a new TriggerField.
39777  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39778  * to the base TextField)
39779  */
39780 Roo.form.TriggerField = function(config){
39781     this.mimicing = false;
39782     Roo.form.TriggerField.superclass.constructor.call(this, config);
39783 };
39784
39785 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39786     /**
39787      * @cfg {String} triggerClass A CSS class to apply to the trigger
39788      */
39789     /**
39790      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39791      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39792      */
39793     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39794     /**
39795      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39796      */
39797     hideTrigger:false,
39798
39799     /** @cfg {Boolean} grow @hide */
39800     /** @cfg {Number} growMin @hide */
39801     /** @cfg {Number} growMax @hide */
39802
39803     /**
39804      * @hide 
39805      * @method
39806      */
39807     autoSize: Roo.emptyFn,
39808     // private
39809     monitorTab : true,
39810     // private
39811     deferHeight : true,
39812
39813     
39814     actionMode : 'wrap',
39815     // private
39816     onResize : function(w, h){
39817         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39818         if(typeof w == 'number'){
39819             var x = w - this.trigger.getWidth();
39820             this.el.setWidth(this.adjustWidth('input', x));
39821             this.trigger.setStyle('left', x+'px');
39822         }
39823     },
39824
39825     // private
39826     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39827
39828     // private
39829     getResizeEl : function(){
39830         return this.wrap;
39831     },
39832
39833     // private
39834     getPositionEl : function(){
39835         return this.wrap;
39836     },
39837
39838     // private
39839     alignErrorIcon : function(){
39840         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39841     },
39842
39843     // private
39844     onRender : function(ct, position){
39845         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39846         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39847         this.trigger = this.wrap.createChild(this.triggerConfig ||
39848                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39849         if(this.hideTrigger){
39850             this.trigger.setDisplayed(false);
39851         }
39852         this.initTrigger();
39853         if(!this.width){
39854             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39855         }
39856     },
39857
39858     // private
39859     initTrigger : function(){
39860         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39861         this.trigger.addClassOnOver('x-form-trigger-over');
39862         this.trigger.addClassOnClick('x-form-trigger-click');
39863     },
39864
39865     // private
39866     onDestroy : function(){
39867         if(this.trigger){
39868             this.trigger.removeAllListeners();
39869             this.trigger.remove();
39870         }
39871         if(this.wrap){
39872             this.wrap.remove();
39873         }
39874         Roo.form.TriggerField.superclass.onDestroy.call(this);
39875     },
39876
39877     // private
39878     onFocus : function(){
39879         Roo.form.TriggerField.superclass.onFocus.call(this);
39880         if(!this.mimicing){
39881             this.wrap.addClass('x-trigger-wrap-focus');
39882             this.mimicing = true;
39883             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39884             if(this.monitorTab){
39885                 this.el.on("keydown", this.checkTab, this);
39886             }
39887         }
39888     },
39889
39890     // private
39891     checkTab : function(e){
39892         if(e.getKey() == e.TAB){
39893             this.triggerBlur();
39894         }
39895     },
39896
39897     // private
39898     onBlur : function(){
39899         // do nothing
39900     },
39901
39902     // private
39903     mimicBlur : function(e, t){
39904         if(!this.wrap.contains(t) && this.validateBlur()){
39905             this.triggerBlur();
39906         }
39907     },
39908
39909     // private
39910     triggerBlur : function(){
39911         this.mimicing = false;
39912         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39913         if(this.monitorTab){
39914             this.el.un("keydown", this.checkTab, this);
39915         }
39916         this.wrap.removeClass('x-trigger-wrap-focus');
39917         Roo.form.TriggerField.superclass.onBlur.call(this);
39918     },
39919
39920     // private
39921     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39922     validateBlur : function(e, t){
39923         return true;
39924     },
39925
39926     // private
39927     onDisable : function(){
39928         Roo.form.TriggerField.superclass.onDisable.call(this);
39929         if(this.wrap){
39930             this.wrap.addClass('x-item-disabled');
39931         }
39932     },
39933
39934     // private
39935     onEnable : function(){
39936         Roo.form.TriggerField.superclass.onEnable.call(this);
39937         if(this.wrap){
39938             this.wrap.removeClass('x-item-disabled');
39939         }
39940     },
39941
39942     // private
39943     onShow : function(){
39944         var ae = this.getActionEl();
39945         
39946         if(ae){
39947             ae.dom.style.display = '';
39948             ae.dom.style.visibility = 'visible';
39949         }
39950     },
39951
39952     // private
39953     
39954     onHide : function(){
39955         var ae = this.getActionEl();
39956         ae.dom.style.display = 'none';
39957     },
39958
39959     /**
39960      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39961      * by an implementing function.
39962      * @method
39963      * @param {EventObject} e
39964      */
39965     onTriggerClick : Roo.emptyFn
39966 });
39967
39968 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39969 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39970 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39971 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39972     initComponent : function(){
39973         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39974
39975         this.triggerConfig = {
39976             tag:'span', cls:'x-form-twin-triggers', cn:[
39977             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39978             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39979         ]};
39980     },
39981
39982     getTrigger : function(index){
39983         return this.triggers[index];
39984     },
39985
39986     initTrigger : function(){
39987         var ts = this.trigger.select('.x-form-trigger', true);
39988         this.wrap.setStyle('overflow', 'hidden');
39989         var triggerField = this;
39990         ts.each(function(t, all, index){
39991             t.hide = function(){
39992                 var w = triggerField.wrap.getWidth();
39993                 this.dom.style.display = 'none';
39994                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39995             };
39996             t.show = function(){
39997                 var w = triggerField.wrap.getWidth();
39998                 this.dom.style.display = '';
39999                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40000             };
40001             var triggerIndex = 'Trigger'+(index+1);
40002
40003             if(this['hide'+triggerIndex]){
40004                 t.dom.style.display = 'none';
40005             }
40006             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40007             t.addClassOnOver('x-form-trigger-over');
40008             t.addClassOnClick('x-form-trigger-click');
40009         }, this);
40010         this.triggers = ts.elements;
40011     },
40012
40013     onTrigger1Click : Roo.emptyFn,
40014     onTrigger2Click : Roo.emptyFn
40015 });/*
40016  * Based on:
40017  * Ext JS Library 1.1.1
40018  * Copyright(c) 2006-2007, Ext JS, LLC.
40019  *
40020  * Originally Released Under LGPL - original licence link has changed is not relivant.
40021  *
40022  * Fork - LGPL
40023  * <script type="text/javascript">
40024  */
40025  
40026 /**
40027  * @class Roo.form.TextArea
40028  * @extends Roo.form.TextField
40029  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40030  * support for auto-sizing.
40031  * @constructor
40032  * Creates a new TextArea
40033  * @param {Object} config Configuration options
40034  */
40035 Roo.form.TextArea = function(config){
40036     Roo.form.TextArea.superclass.constructor.call(this, config);
40037     // these are provided exchanges for backwards compat
40038     // minHeight/maxHeight were replaced by growMin/growMax to be
40039     // compatible with TextField growing config values
40040     if(this.minHeight !== undefined){
40041         this.growMin = this.minHeight;
40042     }
40043     if(this.maxHeight !== undefined){
40044         this.growMax = this.maxHeight;
40045     }
40046 };
40047
40048 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40049     /**
40050      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40051      */
40052     growMin : 60,
40053     /**
40054      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40055      */
40056     growMax: 1000,
40057     /**
40058      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40059      * in the field (equivalent to setting overflow: hidden, defaults to false)
40060      */
40061     preventScrollbars: false,
40062     /**
40063      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40064      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40065      */
40066
40067     // private
40068     onRender : function(ct, position){
40069         if(!this.el){
40070             this.defaultAutoCreate = {
40071                 tag: "textarea",
40072                 style:"width:300px;height:60px;",
40073                 autocomplete: "new-password"
40074             };
40075         }
40076         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40077         if(this.grow){
40078             this.textSizeEl = Roo.DomHelper.append(document.body, {
40079                 tag: "pre", cls: "x-form-grow-sizer"
40080             });
40081             if(this.preventScrollbars){
40082                 this.el.setStyle("overflow", "hidden");
40083             }
40084             this.el.setHeight(this.growMin);
40085         }
40086     },
40087
40088     onDestroy : function(){
40089         if(this.textSizeEl){
40090             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40091         }
40092         Roo.form.TextArea.superclass.onDestroy.call(this);
40093     },
40094
40095     // private
40096     onKeyUp : function(e){
40097         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40098             this.autoSize();
40099         }
40100     },
40101
40102     /**
40103      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40104      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40105      */
40106     autoSize : function(){
40107         if(!this.grow || !this.textSizeEl){
40108             return;
40109         }
40110         var el = this.el;
40111         var v = el.dom.value;
40112         var ts = this.textSizeEl;
40113
40114         ts.innerHTML = '';
40115         ts.appendChild(document.createTextNode(v));
40116         v = ts.innerHTML;
40117
40118         Roo.fly(ts).setWidth(this.el.getWidth());
40119         if(v.length < 1){
40120             v = "&#160;&#160;";
40121         }else{
40122             if(Roo.isIE){
40123                 v = v.replace(/\n/g, '<p>&#160;</p>');
40124             }
40125             v += "&#160;\n&#160;";
40126         }
40127         ts.innerHTML = v;
40128         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40129         if(h != this.lastHeight){
40130             this.lastHeight = h;
40131             this.el.setHeight(h);
40132             this.fireEvent("autosize", this, h);
40133         }
40134     }
40135 });/*
40136  * Based on:
40137  * Ext JS Library 1.1.1
40138  * Copyright(c) 2006-2007, Ext JS, LLC.
40139  *
40140  * Originally Released Under LGPL - original licence link has changed is not relivant.
40141  *
40142  * Fork - LGPL
40143  * <script type="text/javascript">
40144  */
40145  
40146
40147 /**
40148  * @class Roo.form.NumberField
40149  * @extends Roo.form.TextField
40150  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40151  * @constructor
40152  * Creates a new NumberField
40153  * @param {Object} config Configuration options
40154  */
40155 Roo.form.NumberField = function(config){
40156     Roo.form.NumberField.superclass.constructor.call(this, config);
40157 };
40158
40159 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40160     /**
40161      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40162      */
40163     fieldClass: "x-form-field x-form-num-field",
40164     /**
40165      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40166      */
40167     allowDecimals : true,
40168     /**
40169      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40170      */
40171     decimalSeparator : ".",
40172     /**
40173      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40174      */
40175     decimalPrecision : 2,
40176     /**
40177      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40178      */
40179     allowNegative : true,
40180     /**
40181      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40182      */
40183     minValue : Number.NEGATIVE_INFINITY,
40184     /**
40185      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40186      */
40187     maxValue : Number.MAX_VALUE,
40188     /**
40189      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40190      */
40191     minText : "The minimum value for this field is {0}",
40192     /**
40193      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40194      */
40195     maxText : "The maximum value for this field is {0}",
40196     /**
40197      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40198      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40199      */
40200     nanText : "{0} is not a valid number",
40201
40202     // private
40203     initEvents : function(){
40204         Roo.form.NumberField.superclass.initEvents.call(this);
40205         var allowed = "0123456789";
40206         if(this.allowDecimals){
40207             allowed += this.decimalSeparator;
40208         }
40209         if(this.allowNegative){
40210             allowed += "-";
40211         }
40212         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40213         var keyPress = function(e){
40214             var k = e.getKey();
40215             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40216                 return;
40217             }
40218             var c = e.getCharCode();
40219             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40220                 e.stopEvent();
40221             }
40222         };
40223         this.el.on("keypress", keyPress, this);
40224     },
40225
40226     // private
40227     validateValue : function(value){
40228         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40229             return false;
40230         }
40231         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40232              return true;
40233         }
40234         var num = this.parseValue(value);
40235         if(isNaN(num)){
40236             this.markInvalid(String.format(this.nanText, value));
40237             return false;
40238         }
40239         if(num < this.minValue){
40240             this.markInvalid(String.format(this.minText, this.minValue));
40241             return false;
40242         }
40243         if(num > this.maxValue){
40244             this.markInvalid(String.format(this.maxText, this.maxValue));
40245             return false;
40246         }
40247         return true;
40248     },
40249
40250     getValue : function(){
40251         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40252     },
40253
40254     // private
40255     parseValue : function(value){
40256         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40257         return isNaN(value) ? '' : value;
40258     },
40259
40260     // private
40261     fixPrecision : function(value){
40262         var nan = isNaN(value);
40263         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40264             return nan ? '' : value;
40265         }
40266         return parseFloat(value).toFixed(this.decimalPrecision);
40267     },
40268
40269     setValue : function(v){
40270         v = this.fixPrecision(v);
40271         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40272     },
40273
40274     // private
40275     decimalPrecisionFcn : function(v){
40276         return Math.floor(v);
40277     },
40278
40279     beforeBlur : function(){
40280         var v = this.parseValue(this.getRawValue());
40281         if(v){
40282             this.setValue(v);
40283         }
40284     }
40285 });/*
40286  * Based on:
40287  * Ext JS Library 1.1.1
40288  * Copyright(c) 2006-2007, Ext JS, LLC.
40289  *
40290  * Originally Released Under LGPL - original licence link has changed is not relivant.
40291  *
40292  * Fork - LGPL
40293  * <script type="text/javascript">
40294  */
40295  
40296 /**
40297  * @class Roo.form.DateField
40298  * @extends Roo.form.TriggerField
40299  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40300 * @constructor
40301 * Create a new DateField
40302 * @param {Object} config
40303  */
40304 Roo.form.DateField = function(config)
40305 {
40306     Roo.form.DateField.superclass.constructor.call(this, config);
40307     
40308       this.addEvents({
40309          
40310         /**
40311          * @event select
40312          * Fires when a date is selected
40313              * @param {Roo.form.DateField} combo This combo box
40314              * @param {Date} date The date selected
40315              */
40316         'select' : true
40317          
40318     });
40319     
40320     
40321     if(typeof this.minValue == "string") {
40322         this.minValue = this.parseDate(this.minValue);
40323     }
40324     if(typeof this.maxValue == "string") {
40325         this.maxValue = this.parseDate(this.maxValue);
40326     }
40327     this.ddMatch = null;
40328     if(this.disabledDates){
40329         var dd = this.disabledDates;
40330         var re = "(?:";
40331         for(var i = 0; i < dd.length; i++){
40332             re += dd[i];
40333             if(i != dd.length-1) {
40334                 re += "|";
40335             }
40336         }
40337         this.ddMatch = new RegExp(re + ")");
40338     }
40339 };
40340
40341 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40342     /**
40343      * @cfg {String} format
40344      * The default date format string which can be overriden for localization support.  The format must be
40345      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40346      */
40347     format : "m/d/y",
40348     /**
40349      * @cfg {String} altFormats
40350      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40351      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40352      */
40353     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40354     /**
40355      * @cfg {Array} disabledDays
40356      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40357      */
40358     disabledDays : null,
40359     /**
40360      * @cfg {String} disabledDaysText
40361      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40362      */
40363     disabledDaysText : "Disabled",
40364     /**
40365      * @cfg {Array} disabledDates
40366      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40367      * expression so they are very powerful. Some examples:
40368      * <ul>
40369      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40370      * <li>["03/08", "09/16"] would disable those days for every year</li>
40371      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40372      * <li>["03/../2006"] would disable every day in March 2006</li>
40373      * <li>["^03"] would disable every day in every March</li>
40374      * </ul>
40375      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40376      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40377      */
40378     disabledDates : null,
40379     /**
40380      * @cfg {String} disabledDatesText
40381      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40382      */
40383     disabledDatesText : "Disabled",
40384     /**
40385      * @cfg {Date/String} minValue
40386      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40387      * valid format (defaults to null).
40388      */
40389     minValue : null,
40390     /**
40391      * @cfg {Date/String} maxValue
40392      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40393      * valid format (defaults to null).
40394      */
40395     maxValue : null,
40396     /**
40397      * @cfg {String} minText
40398      * The error text to display when the date in the cell is before minValue (defaults to
40399      * 'The date in this field must be after {minValue}').
40400      */
40401     minText : "The date in this field must be equal to or after {0}",
40402     /**
40403      * @cfg {String} maxText
40404      * The error text to display when the date in the cell is after maxValue (defaults to
40405      * 'The date in this field must be before {maxValue}').
40406      */
40407     maxText : "The date in this field must be equal to or before {0}",
40408     /**
40409      * @cfg {String} invalidText
40410      * The error text to display when the date in the field is invalid (defaults to
40411      * '{value} is not a valid date - it must be in the format {format}').
40412      */
40413     invalidText : "{0} is not a valid date - it must be in the format {1}",
40414     /**
40415      * @cfg {String} triggerClass
40416      * An additional CSS class used to style the trigger button.  The trigger will always get the
40417      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40418      * which displays a calendar icon).
40419      */
40420     triggerClass : 'x-form-date-trigger',
40421     
40422
40423     /**
40424      * @cfg {Boolean} useIso
40425      * if enabled, then the date field will use a hidden field to store the 
40426      * real value as iso formated date. default (false)
40427      */ 
40428     useIso : false,
40429     /**
40430      * @cfg {String/Object} autoCreate
40431      * A DomHelper element spec, or true for a default element spec (defaults to
40432      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40433      */ 
40434     // private
40435     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40436     
40437     // private
40438     hiddenField: false,
40439     
40440     onRender : function(ct, position)
40441     {
40442         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40443         if (this.useIso) {
40444             //this.el.dom.removeAttribute('name'); 
40445             Roo.log("Changing name?");
40446             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40447             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40448                     'before', true);
40449             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40450             // prevent input submission
40451             this.hiddenName = this.name;
40452         }
40453             
40454             
40455     },
40456     
40457     // private
40458     validateValue : function(value)
40459     {
40460         value = this.formatDate(value);
40461         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40462             Roo.log('super failed');
40463             return false;
40464         }
40465         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40466              return true;
40467         }
40468         var svalue = value;
40469         value = this.parseDate(value);
40470         if(!value){
40471             Roo.log('parse date failed' + svalue);
40472             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40473             return false;
40474         }
40475         var time = value.getTime();
40476         if(this.minValue && time < this.minValue.getTime()){
40477             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40478             return false;
40479         }
40480         if(this.maxValue && time > this.maxValue.getTime()){
40481             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40482             return false;
40483         }
40484         if(this.disabledDays){
40485             var day = value.getDay();
40486             for(var i = 0; i < this.disabledDays.length; i++) {
40487                 if(day === this.disabledDays[i]){
40488                     this.markInvalid(this.disabledDaysText);
40489                     return false;
40490                 }
40491             }
40492         }
40493         var fvalue = this.formatDate(value);
40494         if(this.ddMatch && this.ddMatch.test(fvalue)){
40495             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40496             return false;
40497         }
40498         return true;
40499     },
40500
40501     // private
40502     // Provides logic to override the default TriggerField.validateBlur which just returns true
40503     validateBlur : function(){
40504         return !this.menu || !this.menu.isVisible();
40505     },
40506     
40507     getName: function()
40508     {
40509         // returns hidden if it's set..
40510         if (!this.rendered) {return ''};
40511         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40512         
40513     },
40514
40515     /**
40516      * Returns the current date value of the date field.
40517      * @return {Date} The date value
40518      */
40519     getValue : function(){
40520         
40521         return  this.hiddenField ?
40522                 this.hiddenField.value :
40523                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40524     },
40525
40526     /**
40527      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40528      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40529      * (the default format used is "m/d/y").
40530      * <br />Usage:
40531      * <pre><code>
40532 //All of these calls set the same date value (May 4, 2006)
40533
40534 //Pass a date object:
40535 var dt = new Date('5/4/06');
40536 dateField.setValue(dt);
40537
40538 //Pass a date string (default format):
40539 dateField.setValue('5/4/06');
40540
40541 //Pass a date string (custom format):
40542 dateField.format = 'Y-m-d';
40543 dateField.setValue('2006-5-4');
40544 </code></pre>
40545      * @param {String/Date} date The date or valid date string
40546      */
40547     setValue : function(date){
40548         if (this.hiddenField) {
40549             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40550         }
40551         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40552         // make sure the value field is always stored as a date..
40553         this.value = this.parseDate(date);
40554         
40555         
40556     },
40557
40558     // private
40559     parseDate : function(value){
40560         if(!value || value instanceof Date){
40561             return value;
40562         }
40563         var v = Date.parseDate(value, this.format);
40564          if (!v && this.useIso) {
40565             v = Date.parseDate(value, 'Y-m-d');
40566         }
40567         if(!v && this.altFormats){
40568             if(!this.altFormatsArray){
40569                 this.altFormatsArray = this.altFormats.split("|");
40570             }
40571             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40572                 v = Date.parseDate(value, this.altFormatsArray[i]);
40573             }
40574         }
40575         return v;
40576     },
40577
40578     // private
40579     formatDate : function(date, fmt){
40580         return (!date || !(date instanceof Date)) ?
40581                date : date.dateFormat(fmt || this.format);
40582     },
40583
40584     // private
40585     menuListeners : {
40586         select: function(m, d){
40587             
40588             this.setValue(d);
40589             this.fireEvent('select', this, d);
40590         },
40591         show : function(){ // retain focus styling
40592             this.onFocus();
40593         },
40594         hide : function(){
40595             this.focus.defer(10, this);
40596             var ml = this.menuListeners;
40597             this.menu.un("select", ml.select,  this);
40598             this.menu.un("show", ml.show,  this);
40599             this.menu.un("hide", ml.hide,  this);
40600         }
40601     },
40602
40603     // private
40604     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40605     onTriggerClick : function(){
40606         if(this.disabled){
40607             return;
40608         }
40609         if(this.menu == null){
40610             this.menu = new Roo.menu.DateMenu();
40611         }
40612         Roo.apply(this.menu.picker,  {
40613             showClear: this.allowBlank,
40614             minDate : this.minValue,
40615             maxDate : this.maxValue,
40616             disabledDatesRE : this.ddMatch,
40617             disabledDatesText : this.disabledDatesText,
40618             disabledDays : this.disabledDays,
40619             disabledDaysText : this.disabledDaysText,
40620             format : this.useIso ? 'Y-m-d' : this.format,
40621             minText : String.format(this.minText, this.formatDate(this.minValue)),
40622             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40623         });
40624         this.menu.on(Roo.apply({}, this.menuListeners, {
40625             scope:this
40626         }));
40627         this.menu.picker.setValue(this.getValue() || new Date());
40628         this.menu.show(this.el, "tl-bl?");
40629     },
40630
40631     beforeBlur : function(){
40632         var v = this.parseDate(this.getRawValue());
40633         if(v){
40634             this.setValue(v);
40635         }
40636     },
40637
40638     /*@
40639      * overide
40640      * 
40641      */
40642     isDirty : function() {
40643         if(this.disabled) {
40644             return false;
40645         }
40646         
40647         if(typeof(this.startValue) === 'undefined'){
40648             return false;
40649         }
40650         
40651         return String(this.getValue()) !== String(this.startValue);
40652         
40653     },
40654     // @overide
40655     cleanLeadingSpace : function(e)
40656     {
40657        return;
40658     }
40659     
40660 });/*
40661  * Based on:
40662  * Ext JS Library 1.1.1
40663  * Copyright(c) 2006-2007, Ext JS, LLC.
40664  *
40665  * Originally Released Under LGPL - original licence link has changed is not relivant.
40666  *
40667  * Fork - LGPL
40668  * <script type="text/javascript">
40669  */
40670  
40671 /**
40672  * @class Roo.form.MonthField
40673  * @extends Roo.form.TriggerField
40674  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40675 * @constructor
40676 * Create a new MonthField
40677 * @param {Object} config
40678  */
40679 Roo.form.MonthField = function(config){
40680     
40681     Roo.form.MonthField.superclass.constructor.call(this, config);
40682     
40683       this.addEvents({
40684          
40685         /**
40686          * @event select
40687          * Fires when a date is selected
40688              * @param {Roo.form.MonthFieeld} combo This combo box
40689              * @param {Date} date The date selected
40690              */
40691         'select' : true
40692          
40693     });
40694     
40695     
40696     if(typeof this.minValue == "string") {
40697         this.minValue = this.parseDate(this.minValue);
40698     }
40699     if(typeof this.maxValue == "string") {
40700         this.maxValue = this.parseDate(this.maxValue);
40701     }
40702     this.ddMatch = null;
40703     if(this.disabledDates){
40704         var dd = this.disabledDates;
40705         var re = "(?:";
40706         for(var i = 0; i < dd.length; i++){
40707             re += dd[i];
40708             if(i != dd.length-1) {
40709                 re += "|";
40710             }
40711         }
40712         this.ddMatch = new RegExp(re + ")");
40713     }
40714 };
40715
40716 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40717     /**
40718      * @cfg {String} format
40719      * The default date format string which can be overriden for localization support.  The format must be
40720      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40721      */
40722     format : "M Y",
40723     /**
40724      * @cfg {String} altFormats
40725      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40726      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40727      */
40728     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40729     /**
40730      * @cfg {Array} disabledDays
40731      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40732      */
40733     disabledDays : [0,1,2,3,4,5,6],
40734     /**
40735      * @cfg {String} disabledDaysText
40736      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40737      */
40738     disabledDaysText : "Disabled",
40739     /**
40740      * @cfg {Array} disabledDates
40741      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40742      * expression so they are very powerful. Some examples:
40743      * <ul>
40744      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40745      * <li>["03/08", "09/16"] would disable those days for every year</li>
40746      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40747      * <li>["03/../2006"] would disable every day in March 2006</li>
40748      * <li>["^03"] would disable every day in every March</li>
40749      * </ul>
40750      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40751      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40752      */
40753     disabledDates : null,
40754     /**
40755      * @cfg {String} disabledDatesText
40756      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40757      */
40758     disabledDatesText : "Disabled",
40759     /**
40760      * @cfg {Date/String} minValue
40761      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40762      * valid format (defaults to null).
40763      */
40764     minValue : null,
40765     /**
40766      * @cfg {Date/String} maxValue
40767      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40768      * valid format (defaults to null).
40769      */
40770     maxValue : null,
40771     /**
40772      * @cfg {String} minText
40773      * The error text to display when the date in the cell is before minValue (defaults to
40774      * 'The date in this field must be after {minValue}').
40775      */
40776     minText : "The date in this field must be equal to or after {0}",
40777     /**
40778      * @cfg {String} maxTextf
40779      * The error text to display when the date in the cell is after maxValue (defaults to
40780      * 'The date in this field must be before {maxValue}').
40781      */
40782     maxText : "The date in this field must be equal to or before {0}",
40783     /**
40784      * @cfg {String} invalidText
40785      * The error text to display when the date in the field is invalid (defaults to
40786      * '{value} is not a valid date - it must be in the format {format}').
40787      */
40788     invalidText : "{0} is not a valid date - it must be in the format {1}",
40789     /**
40790      * @cfg {String} triggerClass
40791      * An additional CSS class used to style the trigger button.  The trigger will always get the
40792      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40793      * which displays a calendar icon).
40794      */
40795     triggerClass : 'x-form-date-trigger',
40796     
40797
40798     /**
40799      * @cfg {Boolean} useIso
40800      * if enabled, then the date field will use a hidden field to store the 
40801      * real value as iso formated date. default (true)
40802      */ 
40803     useIso : true,
40804     /**
40805      * @cfg {String/Object} autoCreate
40806      * A DomHelper element spec, or true for a default element spec (defaults to
40807      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40808      */ 
40809     // private
40810     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40811     
40812     // private
40813     hiddenField: false,
40814     
40815     hideMonthPicker : false,
40816     
40817     onRender : function(ct, position)
40818     {
40819         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40820         if (this.useIso) {
40821             this.el.dom.removeAttribute('name'); 
40822             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40823                     'before', true);
40824             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40825             // prevent input submission
40826             this.hiddenName = this.name;
40827         }
40828             
40829             
40830     },
40831     
40832     // private
40833     validateValue : function(value)
40834     {
40835         value = this.formatDate(value);
40836         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40837             return false;
40838         }
40839         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40840              return true;
40841         }
40842         var svalue = value;
40843         value = this.parseDate(value);
40844         if(!value){
40845             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40846             return false;
40847         }
40848         var time = value.getTime();
40849         if(this.minValue && time < this.minValue.getTime()){
40850             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40851             return false;
40852         }
40853         if(this.maxValue && time > this.maxValue.getTime()){
40854             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40855             return false;
40856         }
40857         /*if(this.disabledDays){
40858             var day = value.getDay();
40859             for(var i = 0; i < this.disabledDays.length; i++) {
40860                 if(day === this.disabledDays[i]){
40861                     this.markInvalid(this.disabledDaysText);
40862                     return false;
40863                 }
40864             }
40865         }
40866         */
40867         var fvalue = this.formatDate(value);
40868         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40869             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40870             return false;
40871         }
40872         */
40873         return true;
40874     },
40875
40876     // private
40877     // Provides logic to override the default TriggerField.validateBlur which just returns true
40878     validateBlur : function(){
40879         return !this.menu || !this.menu.isVisible();
40880     },
40881
40882     /**
40883      * Returns the current date value of the date field.
40884      * @return {Date} The date value
40885      */
40886     getValue : function(){
40887         
40888         
40889         
40890         return  this.hiddenField ?
40891                 this.hiddenField.value :
40892                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40893     },
40894
40895     /**
40896      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40897      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40898      * (the default format used is "m/d/y").
40899      * <br />Usage:
40900      * <pre><code>
40901 //All of these calls set the same date value (May 4, 2006)
40902
40903 //Pass a date object:
40904 var dt = new Date('5/4/06');
40905 monthField.setValue(dt);
40906
40907 //Pass a date string (default format):
40908 monthField.setValue('5/4/06');
40909
40910 //Pass a date string (custom format):
40911 monthField.format = 'Y-m-d';
40912 monthField.setValue('2006-5-4');
40913 </code></pre>
40914      * @param {String/Date} date The date or valid date string
40915      */
40916     setValue : function(date){
40917         Roo.log('month setValue' + date);
40918         // can only be first of month..
40919         
40920         var val = this.parseDate(date);
40921         
40922         if (this.hiddenField) {
40923             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40924         }
40925         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40926         this.value = this.parseDate(date);
40927     },
40928
40929     // private
40930     parseDate : function(value){
40931         if(!value || value instanceof Date){
40932             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40933             return value;
40934         }
40935         var v = Date.parseDate(value, this.format);
40936         if (!v && this.useIso) {
40937             v = Date.parseDate(value, 'Y-m-d');
40938         }
40939         if (v) {
40940             // 
40941             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40942         }
40943         
40944         
40945         if(!v && this.altFormats){
40946             if(!this.altFormatsArray){
40947                 this.altFormatsArray = this.altFormats.split("|");
40948             }
40949             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40950                 v = Date.parseDate(value, this.altFormatsArray[i]);
40951             }
40952         }
40953         return v;
40954     },
40955
40956     // private
40957     formatDate : function(date, fmt){
40958         return (!date || !(date instanceof Date)) ?
40959                date : date.dateFormat(fmt || this.format);
40960     },
40961
40962     // private
40963     menuListeners : {
40964         select: function(m, d){
40965             this.setValue(d);
40966             this.fireEvent('select', this, d);
40967         },
40968         show : function(){ // retain focus styling
40969             this.onFocus();
40970         },
40971         hide : function(){
40972             this.focus.defer(10, this);
40973             var ml = this.menuListeners;
40974             this.menu.un("select", ml.select,  this);
40975             this.menu.un("show", ml.show,  this);
40976             this.menu.un("hide", ml.hide,  this);
40977         }
40978     },
40979     // private
40980     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40981     onTriggerClick : function(){
40982         if(this.disabled){
40983             return;
40984         }
40985         if(this.menu == null){
40986             this.menu = new Roo.menu.DateMenu();
40987            
40988         }
40989         
40990         Roo.apply(this.menu.picker,  {
40991             
40992             showClear: this.allowBlank,
40993             minDate : this.minValue,
40994             maxDate : this.maxValue,
40995             disabledDatesRE : this.ddMatch,
40996             disabledDatesText : this.disabledDatesText,
40997             
40998             format : this.useIso ? 'Y-m-d' : this.format,
40999             minText : String.format(this.minText, this.formatDate(this.minValue)),
41000             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41001             
41002         });
41003          this.menu.on(Roo.apply({}, this.menuListeners, {
41004             scope:this
41005         }));
41006        
41007         
41008         var m = this.menu;
41009         var p = m.picker;
41010         
41011         // hide month picker get's called when we called by 'before hide';
41012         
41013         var ignorehide = true;
41014         p.hideMonthPicker  = function(disableAnim){
41015             if (ignorehide) {
41016                 return;
41017             }
41018              if(this.monthPicker){
41019                 Roo.log("hideMonthPicker called");
41020                 if(disableAnim === true){
41021                     this.monthPicker.hide();
41022                 }else{
41023                     this.monthPicker.slideOut('t', {duration:.2});
41024                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41025                     p.fireEvent("select", this, this.value);
41026                     m.hide();
41027                 }
41028             }
41029         }
41030         
41031         Roo.log('picker set value');
41032         Roo.log(this.getValue());
41033         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41034         m.show(this.el, 'tl-bl?');
41035         ignorehide  = false;
41036         // this will trigger hideMonthPicker..
41037         
41038         
41039         // hidden the day picker
41040         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41041         
41042         
41043         
41044       
41045         
41046         p.showMonthPicker.defer(100, p);
41047     
41048         
41049        
41050     },
41051
41052     beforeBlur : function(){
41053         var v = this.parseDate(this.getRawValue());
41054         if(v){
41055             this.setValue(v);
41056         }
41057     }
41058
41059     /** @cfg {Boolean} grow @hide */
41060     /** @cfg {Number} growMin @hide */
41061     /** @cfg {Number} growMax @hide */
41062     /**
41063      * @hide
41064      * @method autoSize
41065      */
41066 });/*
41067  * Based on:
41068  * Ext JS Library 1.1.1
41069  * Copyright(c) 2006-2007, Ext JS, LLC.
41070  *
41071  * Originally Released Under LGPL - original licence link has changed is not relivant.
41072  *
41073  * Fork - LGPL
41074  * <script type="text/javascript">
41075  */
41076  
41077
41078 /**
41079  * @class Roo.form.ComboBox
41080  * @extends Roo.form.TriggerField
41081  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41082  * @constructor
41083  * Create a new ComboBox.
41084  * @param {Object} config Configuration options
41085  */
41086 Roo.form.ComboBox = function(config){
41087     Roo.form.ComboBox.superclass.constructor.call(this, config);
41088     this.addEvents({
41089         /**
41090          * @event expand
41091          * Fires when the dropdown list is expanded
41092              * @param {Roo.form.ComboBox} combo This combo box
41093              */
41094         'expand' : true,
41095         /**
41096          * @event collapse
41097          * Fires when the dropdown list is collapsed
41098              * @param {Roo.form.ComboBox} combo This combo box
41099              */
41100         'collapse' : true,
41101         /**
41102          * @event beforeselect
41103          * Fires before a list item is selected. Return false to cancel the selection.
41104              * @param {Roo.form.ComboBox} combo This combo box
41105              * @param {Roo.data.Record} record The data record returned from the underlying store
41106              * @param {Number} index The index of the selected item in the dropdown list
41107              */
41108         'beforeselect' : true,
41109         /**
41110          * @event select
41111          * Fires when a list item is selected
41112              * @param {Roo.form.ComboBox} combo This combo box
41113              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41114              * @param {Number} index The index of the selected item in the dropdown list
41115              */
41116         'select' : true,
41117         /**
41118          * @event beforequery
41119          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41120          * The event object passed has these properties:
41121              * @param {Roo.form.ComboBox} combo This combo box
41122              * @param {String} query The query
41123              * @param {Boolean} forceAll true to force "all" query
41124              * @param {Boolean} cancel true to cancel the query
41125              * @param {Object} e The query event object
41126              */
41127         'beforequery': true,
41128          /**
41129          * @event add
41130          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41131              * @param {Roo.form.ComboBox} combo This combo box
41132              */
41133         'add' : true,
41134         /**
41135          * @event edit
41136          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41137              * @param {Roo.form.ComboBox} combo This combo box
41138              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41139              */
41140         'edit' : true
41141         
41142         
41143     });
41144     if(this.transform){
41145         this.allowDomMove = false;
41146         var s = Roo.getDom(this.transform);
41147         if(!this.hiddenName){
41148             this.hiddenName = s.name;
41149         }
41150         if(!this.store){
41151             this.mode = 'local';
41152             var d = [], opts = s.options;
41153             for(var i = 0, len = opts.length;i < len; i++){
41154                 var o = opts[i];
41155                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41156                 if(o.selected) {
41157                     this.value = value;
41158                 }
41159                 d.push([value, o.text]);
41160             }
41161             this.store = new Roo.data.SimpleStore({
41162                 'id': 0,
41163                 fields: ['value', 'text'],
41164                 data : d
41165             });
41166             this.valueField = 'value';
41167             this.displayField = 'text';
41168         }
41169         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41170         if(!this.lazyRender){
41171             this.target = true;
41172             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41173             s.parentNode.removeChild(s); // remove it
41174             this.render(this.el.parentNode);
41175         }else{
41176             s.parentNode.removeChild(s); // remove it
41177         }
41178
41179     }
41180     if (this.store) {
41181         this.store = Roo.factory(this.store, Roo.data);
41182     }
41183     
41184     this.selectedIndex = -1;
41185     if(this.mode == 'local'){
41186         if(config.queryDelay === undefined){
41187             this.queryDelay = 10;
41188         }
41189         if(config.minChars === undefined){
41190             this.minChars = 0;
41191         }
41192     }
41193 };
41194
41195 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41196     /**
41197      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41198      */
41199     /**
41200      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41201      * rendering into an Roo.Editor, defaults to false)
41202      */
41203     /**
41204      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41205      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41206      */
41207     /**
41208      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41209      */
41210     /**
41211      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41212      * the dropdown list (defaults to undefined, with no header element)
41213      */
41214
41215      /**
41216      * @cfg {String/Roo.Template} tpl The template to use to render the output
41217      */
41218      
41219     // private
41220     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41221     /**
41222      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41223      */
41224     listWidth: undefined,
41225     /**
41226      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41227      * mode = 'remote' or 'text' if mode = 'local')
41228      */
41229     displayField: undefined,
41230     /**
41231      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41232      * mode = 'remote' or 'value' if mode = 'local'). 
41233      * Note: use of a valueField requires the user make a selection
41234      * in order for a value to be mapped.
41235      */
41236     valueField: undefined,
41237     
41238     
41239     /**
41240      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41241      * field's data value (defaults to the underlying DOM element's name)
41242      */
41243     hiddenName: undefined,
41244     /**
41245      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41246      */
41247     listClass: '',
41248     /**
41249      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41250      */
41251     selectedClass: 'x-combo-selected',
41252     /**
41253      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41254      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41255      * which displays a downward arrow icon).
41256      */
41257     triggerClass : 'x-form-arrow-trigger',
41258     /**
41259      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41260      */
41261     shadow:'sides',
41262     /**
41263      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41264      * anchor positions (defaults to 'tl-bl')
41265      */
41266     listAlign: 'tl-bl?',
41267     /**
41268      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41269      */
41270     maxHeight: 300,
41271     /**
41272      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41273      * query specified by the allQuery config option (defaults to 'query')
41274      */
41275     triggerAction: 'query',
41276     /**
41277      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41278      * (defaults to 4, does not apply if editable = false)
41279      */
41280     minChars : 4,
41281     /**
41282      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41283      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41284      */
41285     typeAhead: false,
41286     /**
41287      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41288      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41289      */
41290     queryDelay: 500,
41291     /**
41292      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41293      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41294      */
41295     pageSize: 0,
41296     /**
41297      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41298      * when editable = true (defaults to false)
41299      */
41300     selectOnFocus:false,
41301     /**
41302      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41303      */
41304     queryParam: 'query',
41305     /**
41306      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41307      * when mode = 'remote' (defaults to 'Loading...')
41308      */
41309     loadingText: 'Loading...',
41310     /**
41311      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41312      */
41313     resizable: false,
41314     /**
41315      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41316      */
41317     handleHeight : 8,
41318     /**
41319      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41320      * traditional select (defaults to true)
41321      */
41322     editable: true,
41323     /**
41324      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41325      */
41326     allQuery: '',
41327     /**
41328      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41329      */
41330     mode: 'remote',
41331     /**
41332      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41333      * listWidth has a higher value)
41334      */
41335     minListWidth : 70,
41336     /**
41337      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41338      * allow the user to set arbitrary text into the field (defaults to false)
41339      */
41340     forceSelection:false,
41341     /**
41342      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41343      * if typeAhead = true (defaults to 250)
41344      */
41345     typeAheadDelay : 250,
41346     /**
41347      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41348      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41349      */
41350     valueNotFoundText : undefined,
41351     /**
41352      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41353      */
41354     blockFocus : false,
41355     
41356     /**
41357      * @cfg {Boolean} disableClear Disable showing of clear button.
41358      */
41359     disableClear : false,
41360     /**
41361      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41362      */
41363     alwaysQuery : false,
41364     
41365     //private
41366     addicon : false,
41367     editicon: false,
41368     
41369     // element that contains real text value.. (when hidden is used..)
41370      
41371     // private
41372     onRender : function(ct, position)
41373     {
41374         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41375         
41376         if(this.hiddenName){
41377             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41378                     'before', true);
41379             this.hiddenField.value =
41380                 this.hiddenValue !== undefined ? this.hiddenValue :
41381                 this.value !== undefined ? this.value : '';
41382
41383             // prevent input submission
41384             this.el.dom.removeAttribute('name');
41385              
41386              
41387         }
41388         
41389         if(Roo.isGecko){
41390             this.el.dom.setAttribute('autocomplete', 'off');
41391         }
41392
41393         var cls = 'x-combo-list';
41394
41395         this.list = new Roo.Layer({
41396             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41397         });
41398
41399         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41400         this.list.setWidth(lw);
41401         this.list.swallowEvent('mousewheel');
41402         this.assetHeight = 0;
41403
41404         if(this.title){
41405             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41406             this.assetHeight += this.header.getHeight();
41407         }
41408
41409         this.innerList = this.list.createChild({cls:cls+'-inner'});
41410         this.innerList.on('mouseover', this.onViewOver, this);
41411         this.innerList.on('mousemove', this.onViewMove, this);
41412         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41413         
41414         if(this.allowBlank && !this.pageSize && !this.disableClear){
41415             this.footer = this.list.createChild({cls:cls+'-ft'});
41416             this.pageTb = new Roo.Toolbar(this.footer);
41417            
41418         }
41419         if(this.pageSize){
41420             this.footer = this.list.createChild({cls:cls+'-ft'});
41421             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41422                     {pageSize: this.pageSize});
41423             
41424         }
41425         
41426         if (this.pageTb && this.allowBlank && !this.disableClear) {
41427             var _this = this;
41428             this.pageTb.add(new Roo.Toolbar.Fill(), {
41429                 cls: 'x-btn-icon x-btn-clear',
41430                 text: '&#160;',
41431                 handler: function()
41432                 {
41433                     _this.collapse();
41434                     _this.clearValue();
41435                     _this.onSelect(false, -1);
41436                 }
41437             });
41438         }
41439         if (this.footer) {
41440             this.assetHeight += this.footer.getHeight();
41441         }
41442         
41443
41444         if(!this.tpl){
41445             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41446         }
41447
41448         this.view = new Roo.View(this.innerList, this.tpl, {
41449             singleSelect:true,
41450             store: this.store,
41451             selectedClass: this.selectedClass
41452         });
41453
41454         this.view.on('click', this.onViewClick, this);
41455
41456         this.store.on('beforeload', this.onBeforeLoad, this);
41457         this.store.on('load', this.onLoad, this);
41458         this.store.on('loadexception', this.onLoadException, this);
41459
41460         if(this.resizable){
41461             this.resizer = new Roo.Resizable(this.list,  {
41462                pinned:true, handles:'se'
41463             });
41464             this.resizer.on('resize', function(r, w, h){
41465                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41466                 this.listWidth = w;
41467                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41468                 this.restrictHeight();
41469             }, this);
41470             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41471         }
41472         if(!this.editable){
41473             this.editable = true;
41474             this.setEditable(false);
41475         }  
41476         
41477         
41478         if (typeof(this.events.add.listeners) != 'undefined') {
41479             
41480             this.addicon = this.wrap.createChild(
41481                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41482        
41483             this.addicon.on('click', function(e) {
41484                 this.fireEvent('add', this);
41485             }, this);
41486         }
41487         if (typeof(this.events.edit.listeners) != 'undefined') {
41488             
41489             this.editicon = this.wrap.createChild(
41490                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41491             if (this.addicon) {
41492                 this.editicon.setStyle('margin-left', '40px');
41493             }
41494             this.editicon.on('click', function(e) {
41495                 
41496                 // we fire even  if inothing is selected..
41497                 this.fireEvent('edit', this, this.lastData );
41498                 
41499             }, this);
41500         }
41501         
41502         
41503         
41504     },
41505
41506     // private
41507     initEvents : function(){
41508         Roo.form.ComboBox.superclass.initEvents.call(this);
41509
41510         this.keyNav = new Roo.KeyNav(this.el, {
41511             "up" : function(e){
41512                 this.inKeyMode = true;
41513                 this.selectPrev();
41514             },
41515
41516             "down" : function(e){
41517                 if(!this.isExpanded()){
41518                     this.onTriggerClick();
41519                 }else{
41520                     this.inKeyMode = true;
41521                     this.selectNext();
41522                 }
41523             },
41524
41525             "enter" : function(e){
41526                 this.onViewClick();
41527                 //return true;
41528             },
41529
41530             "esc" : function(e){
41531                 this.collapse();
41532             },
41533
41534             "tab" : function(e){
41535                 this.onViewClick(false);
41536                 this.fireEvent("specialkey", this, e);
41537                 return true;
41538             },
41539
41540             scope : this,
41541
41542             doRelay : function(foo, bar, hname){
41543                 if(hname == 'down' || this.scope.isExpanded()){
41544                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41545                 }
41546                 return true;
41547             },
41548
41549             forceKeyDown: true
41550         });
41551         this.queryDelay = Math.max(this.queryDelay || 10,
41552                 this.mode == 'local' ? 10 : 250);
41553         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41554         if(this.typeAhead){
41555             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41556         }
41557         if(this.editable !== false){
41558             this.el.on("keyup", this.onKeyUp, this);
41559         }
41560         if(this.forceSelection){
41561             this.on('blur', this.doForce, this);
41562         }
41563     },
41564
41565     onDestroy : function(){
41566         if(this.view){
41567             this.view.setStore(null);
41568             this.view.el.removeAllListeners();
41569             this.view.el.remove();
41570             this.view.purgeListeners();
41571         }
41572         if(this.list){
41573             this.list.destroy();
41574         }
41575         if(this.store){
41576             this.store.un('beforeload', this.onBeforeLoad, this);
41577             this.store.un('load', this.onLoad, this);
41578             this.store.un('loadexception', this.onLoadException, this);
41579         }
41580         Roo.form.ComboBox.superclass.onDestroy.call(this);
41581     },
41582
41583     // private
41584     fireKey : function(e){
41585         if(e.isNavKeyPress() && !this.list.isVisible()){
41586             this.fireEvent("specialkey", this, e);
41587         }
41588     },
41589
41590     // private
41591     onResize: function(w, h){
41592         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41593         
41594         if(typeof w != 'number'){
41595             // we do not handle it!?!?
41596             return;
41597         }
41598         var tw = this.trigger.getWidth();
41599         tw += this.addicon ? this.addicon.getWidth() : 0;
41600         tw += this.editicon ? this.editicon.getWidth() : 0;
41601         var x = w - tw;
41602         this.el.setWidth( this.adjustWidth('input', x));
41603             
41604         this.trigger.setStyle('left', x+'px');
41605         
41606         if(this.list && this.listWidth === undefined){
41607             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41608             this.list.setWidth(lw);
41609             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41610         }
41611         
41612     
41613         
41614     },
41615
41616     /**
41617      * Allow or prevent the user from directly editing the field text.  If false is passed,
41618      * the user will only be able to select from the items defined in the dropdown list.  This method
41619      * is the runtime equivalent of setting the 'editable' config option at config time.
41620      * @param {Boolean} value True to allow the user to directly edit the field text
41621      */
41622     setEditable : function(value){
41623         if(value == this.editable){
41624             return;
41625         }
41626         this.editable = value;
41627         if(!value){
41628             this.el.dom.setAttribute('readOnly', true);
41629             this.el.on('mousedown', this.onTriggerClick,  this);
41630             this.el.addClass('x-combo-noedit');
41631         }else{
41632             this.el.dom.setAttribute('readOnly', false);
41633             this.el.un('mousedown', this.onTriggerClick,  this);
41634             this.el.removeClass('x-combo-noedit');
41635         }
41636     },
41637
41638     // private
41639     onBeforeLoad : function(){
41640         if(!this.hasFocus){
41641             return;
41642         }
41643         this.innerList.update(this.loadingText ?
41644                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41645         this.restrictHeight();
41646         this.selectedIndex = -1;
41647     },
41648
41649     // private
41650     onLoad : function(){
41651         if(!this.hasFocus){
41652             return;
41653         }
41654         if(this.store.getCount() > 0){
41655             this.expand();
41656             this.restrictHeight();
41657             if(this.lastQuery == this.allQuery){
41658                 if(this.editable){
41659                     this.el.dom.select();
41660                 }
41661                 if(!this.selectByValue(this.value, true)){
41662                     this.select(0, true);
41663                 }
41664             }else{
41665                 this.selectNext();
41666                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41667                     this.taTask.delay(this.typeAheadDelay);
41668                 }
41669             }
41670         }else{
41671             this.onEmptyResults();
41672         }
41673         //this.el.focus();
41674     },
41675     // private
41676     onLoadException : function()
41677     {
41678         this.collapse();
41679         Roo.log(this.store.reader.jsonData);
41680         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41681             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41682         }
41683         
41684         
41685     },
41686     // private
41687     onTypeAhead : function(){
41688         if(this.store.getCount() > 0){
41689             var r = this.store.getAt(0);
41690             var newValue = r.data[this.displayField];
41691             var len = newValue.length;
41692             var selStart = this.getRawValue().length;
41693             if(selStart != len){
41694                 this.setRawValue(newValue);
41695                 this.selectText(selStart, newValue.length);
41696             }
41697         }
41698     },
41699
41700     // private
41701     onSelect : function(record, index){
41702         if(this.fireEvent('beforeselect', this, record, index) !== false){
41703             this.setFromData(index > -1 ? record.data : false);
41704             this.collapse();
41705             this.fireEvent('select', this, record, index);
41706         }
41707     },
41708
41709     /**
41710      * Returns the currently selected field value or empty string if no value is set.
41711      * @return {String} value The selected value
41712      */
41713     getValue : function(){
41714         if(this.valueField){
41715             return typeof this.value != 'undefined' ? this.value : '';
41716         }
41717         return Roo.form.ComboBox.superclass.getValue.call(this);
41718     },
41719
41720     /**
41721      * Clears any text/value currently set in the field
41722      */
41723     clearValue : function(){
41724         if(this.hiddenField){
41725             this.hiddenField.value = '';
41726         }
41727         this.value = '';
41728         this.setRawValue('');
41729         this.lastSelectionText = '';
41730         
41731     },
41732
41733     /**
41734      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41735      * will be displayed in the field.  If the value does not match the data value of an existing item,
41736      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41737      * Otherwise the field will be blank (although the value will still be set).
41738      * @param {String} value The value to match
41739      */
41740     setValue : function(v){
41741         var text = v;
41742         if(this.valueField){
41743             var r = this.findRecord(this.valueField, v);
41744             if(r){
41745                 text = r.data[this.displayField];
41746             }else if(this.valueNotFoundText !== undefined){
41747                 text = this.valueNotFoundText;
41748             }
41749         }
41750         this.lastSelectionText = text;
41751         if(this.hiddenField){
41752             this.hiddenField.value = v;
41753         }
41754         Roo.form.ComboBox.superclass.setValue.call(this, text);
41755         this.value = v;
41756     },
41757     /**
41758      * @property {Object} the last set data for the element
41759      */
41760     
41761     lastData : false,
41762     /**
41763      * Sets the value of the field based on a object which is related to the record format for the store.
41764      * @param {Object} value the value to set as. or false on reset?
41765      */
41766     setFromData : function(o){
41767         var dv = ''; // display value
41768         var vv = ''; // value value..
41769         this.lastData = o;
41770         if (this.displayField) {
41771             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41772         } else {
41773             // this is an error condition!!!
41774             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41775         }
41776         
41777         if(this.valueField){
41778             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41779         }
41780         if(this.hiddenField){
41781             this.hiddenField.value = vv;
41782             
41783             this.lastSelectionText = dv;
41784             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41785             this.value = vv;
41786             return;
41787         }
41788         // no hidden field.. - we store the value in 'value', but still display
41789         // display field!!!!
41790         this.lastSelectionText = dv;
41791         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41792         this.value = vv;
41793         
41794         
41795     },
41796     // private
41797     reset : function(){
41798         // overridden so that last data is reset..
41799         this.setValue(this.resetValue);
41800         this.originalValue = this.getValue();
41801         this.clearInvalid();
41802         this.lastData = false;
41803         if (this.view) {
41804             this.view.clearSelections();
41805         }
41806     },
41807     // private
41808     findRecord : function(prop, value){
41809         var record;
41810         if(this.store.getCount() > 0){
41811             this.store.each(function(r){
41812                 if(r.data[prop] == value){
41813                     record = r;
41814                     return false;
41815                 }
41816                 return true;
41817             });
41818         }
41819         return record;
41820     },
41821     
41822     getName: function()
41823     {
41824         // returns hidden if it's set..
41825         if (!this.rendered) {return ''};
41826         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41827         
41828     },
41829     // private
41830     onViewMove : function(e, t){
41831         this.inKeyMode = false;
41832     },
41833
41834     // private
41835     onViewOver : function(e, t){
41836         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41837             return;
41838         }
41839         var item = this.view.findItemFromChild(t);
41840         if(item){
41841             var index = this.view.indexOf(item);
41842             this.select(index, false);
41843         }
41844     },
41845
41846     // private
41847     onViewClick : function(doFocus)
41848     {
41849         var index = this.view.getSelectedIndexes()[0];
41850         var r = this.store.getAt(index);
41851         if(r){
41852             this.onSelect(r, index);
41853         }
41854         if(doFocus !== false && !this.blockFocus){
41855             this.el.focus();
41856         }
41857     },
41858
41859     // private
41860     restrictHeight : function(){
41861         this.innerList.dom.style.height = '';
41862         var inner = this.innerList.dom;
41863         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41864         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41865         this.list.beginUpdate();
41866         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41867         this.list.alignTo(this.el, this.listAlign);
41868         this.list.endUpdate();
41869     },
41870
41871     // private
41872     onEmptyResults : function(){
41873         this.collapse();
41874     },
41875
41876     /**
41877      * Returns true if the dropdown list is expanded, else false.
41878      */
41879     isExpanded : function(){
41880         return this.list.isVisible();
41881     },
41882
41883     /**
41884      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41885      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41886      * @param {String} value The data value of the item to select
41887      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41888      * selected item if it is not currently in view (defaults to true)
41889      * @return {Boolean} True if the value matched an item in the list, else false
41890      */
41891     selectByValue : function(v, scrollIntoView){
41892         if(v !== undefined && v !== null){
41893             var r = this.findRecord(this.valueField || this.displayField, v);
41894             if(r){
41895                 this.select(this.store.indexOf(r), scrollIntoView);
41896                 return true;
41897             }
41898         }
41899         return false;
41900     },
41901
41902     /**
41903      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41904      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41905      * @param {Number} index The zero-based index of the list item to select
41906      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41907      * selected item if it is not currently in view (defaults to true)
41908      */
41909     select : function(index, scrollIntoView){
41910         this.selectedIndex = index;
41911         this.view.select(index);
41912         if(scrollIntoView !== false){
41913             var el = this.view.getNode(index);
41914             if(el){
41915                 this.innerList.scrollChildIntoView(el, false);
41916             }
41917         }
41918     },
41919
41920     // private
41921     selectNext : function(){
41922         var ct = this.store.getCount();
41923         if(ct > 0){
41924             if(this.selectedIndex == -1){
41925                 this.select(0);
41926             }else if(this.selectedIndex < ct-1){
41927                 this.select(this.selectedIndex+1);
41928             }
41929         }
41930     },
41931
41932     // private
41933     selectPrev : function(){
41934         var ct = this.store.getCount();
41935         if(ct > 0){
41936             if(this.selectedIndex == -1){
41937                 this.select(0);
41938             }else if(this.selectedIndex != 0){
41939                 this.select(this.selectedIndex-1);
41940             }
41941         }
41942     },
41943
41944     // private
41945     onKeyUp : function(e){
41946         if(this.editable !== false && !e.isSpecialKey()){
41947             this.lastKey = e.getKey();
41948             this.dqTask.delay(this.queryDelay);
41949         }
41950     },
41951
41952     // private
41953     validateBlur : function(){
41954         return !this.list || !this.list.isVisible();   
41955     },
41956
41957     // private
41958     initQuery : function(){
41959         this.doQuery(this.getRawValue());
41960     },
41961
41962     // private
41963     doForce : function(){
41964         if(this.el.dom.value.length > 0){
41965             this.el.dom.value =
41966                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41967              
41968         }
41969     },
41970
41971     /**
41972      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41973      * query allowing the query action to be canceled if needed.
41974      * @param {String} query The SQL query to execute
41975      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41976      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41977      * saved in the current store (defaults to false)
41978      */
41979     doQuery : function(q, forceAll){
41980         if(q === undefined || q === null){
41981             q = '';
41982         }
41983         var qe = {
41984             query: q,
41985             forceAll: forceAll,
41986             combo: this,
41987             cancel:false
41988         };
41989         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41990             return false;
41991         }
41992         q = qe.query;
41993         forceAll = qe.forceAll;
41994         if(forceAll === true || (q.length >= this.minChars)){
41995             if(this.lastQuery != q || this.alwaysQuery){
41996                 this.lastQuery = q;
41997                 if(this.mode == 'local'){
41998                     this.selectedIndex = -1;
41999                     if(forceAll){
42000                         this.store.clearFilter();
42001                     }else{
42002                         this.store.filter(this.displayField, q);
42003                     }
42004                     this.onLoad();
42005                 }else{
42006                     this.store.baseParams[this.queryParam] = q;
42007                     this.store.load({
42008                         params: this.getParams(q)
42009                     });
42010                     this.expand();
42011                 }
42012             }else{
42013                 this.selectedIndex = -1;
42014                 this.onLoad();   
42015             }
42016         }
42017     },
42018
42019     // private
42020     getParams : function(q){
42021         var p = {};
42022         //p[this.queryParam] = q;
42023         if(this.pageSize){
42024             p.start = 0;
42025             p.limit = this.pageSize;
42026         }
42027         return p;
42028     },
42029
42030     /**
42031      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42032      */
42033     collapse : function(){
42034         if(!this.isExpanded()){
42035             return;
42036         }
42037         this.list.hide();
42038         Roo.get(document).un('mousedown', this.collapseIf, this);
42039         Roo.get(document).un('mousewheel', this.collapseIf, this);
42040         if (!this.editable) {
42041             Roo.get(document).un('keydown', this.listKeyPress, this);
42042         }
42043         this.fireEvent('collapse', this);
42044     },
42045
42046     // private
42047     collapseIf : function(e){
42048         if(!e.within(this.wrap) && !e.within(this.list)){
42049             this.collapse();
42050         }
42051     },
42052
42053     /**
42054      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42055      */
42056     expand : function(){
42057         if(this.isExpanded() || !this.hasFocus){
42058             return;
42059         }
42060         this.list.alignTo(this.el, this.listAlign);
42061         this.list.show();
42062         Roo.get(document).on('mousedown', this.collapseIf, this);
42063         Roo.get(document).on('mousewheel', this.collapseIf, this);
42064         if (!this.editable) {
42065             Roo.get(document).on('keydown', this.listKeyPress, this);
42066         }
42067         
42068         this.fireEvent('expand', this);
42069     },
42070
42071     // private
42072     // Implements the default empty TriggerField.onTriggerClick function
42073     onTriggerClick : function(){
42074         if(this.disabled){
42075             return;
42076         }
42077         if(this.isExpanded()){
42078             this.collapse();
42079             if (!this.blockFocus) {
42080                 this.el.focus();
42081             }
42082             
42083         }else {
42084             this.hasFocus = true;
42085             if(this.triggerAction == 'all') {
42086                 this.doQuery(this.allQuery, true);
42087             } else {
42088                 this.doQuery(this.getRawValue());
42089             }
42090             if (!this.blockFocus) {
42091                 this.el.focus();
42092             }
42093         }
42094     },
42095     listKeyPress : function(e)
42096     {
42097         //Roo.log('listkeypress');
42098         // scroll to first matching element based on key pres..
42099         if (e.isSpecialKey()) {
42100             return false;
42101         }
42102         var k = String.fromCharCode(e.getKey()).toUpperCase();
42103         //Roo.log(k);
42104         var match  = false;
42105         var csel = this.view.getSelectedNodes();
42106         var cselitem = false;
42107         if (csel.length) {
42108             var ix = this.view.indexOf(csel[0]);
42109             cselitem  = this.store.getAt(ix);
42110             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42111                 cselitem = false;
42112             }
42113             
42114         }
42115         
42116         this.store.each(function(v) { 
42117             if (cselitem) {
42118                 // start at existing selection.
42119                 if (cselitem.id == v.id) {
42120                     cselitem = false;
42121                 }
42122                 return;
42123             }
42124                 
42125             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42126                 match = this.store.indexOf(v);
42127                 return false;
42128             }
42129         }, this);
42130         
42131         if (match === false) {
42132             return true; // no more action?
42133         }
42134         // scroll to?
42135         this.view.select(match);
42136         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42137         sn.scrollIntoView(sn.dom.parentNode, false);
42138     } 
42139
42140     /** 
42141     * @cfg {Boolean} grow 
42142     * @hide 
42143     */
42144     /** 
42145     * @cfg {Number} growMin 
42146     * @hide 
42147     */
42148     /** 
42149     * @cfg {Number} growMax 
42150     * @hide 
42151     */
42152     /**
42153      * @hide
42154      * @method autoSize
42155      */
42156 });/*
42157  * Copyright(c) 2010-2012, Roo J Solutions Limited
42158  *
42159  * Licence LGPL
42160  *
42161  */
42162
42163 /**
42164  * @class Roo.form.ComboBoxArray
42165  * @extends Roo.form.TextField
42166  * A facebook style adder... for lists of email / people / countries  etc...
42167  * pick multiple items from a combo box, and shows each one.
42168  *
42169  *  Fred [x]  Brian [x]  [Pick another |v]
42170  *
42171  *
42172  *  For this to work: it needs various extra information
42173  *    - normal combo problay has
42174  *      name, hiddenName
42175  *    + displayField, valueField
42176  *
42177  *    For our purpose...
42178  *
42179  *
42180  *   If we change from 'extends' to wrapping...
42181  *   
42182  *  
42183  *
42184  
42185  
42186  * @constructor
42187  * Create a new ComboBoxArray.
42188  * @param {Object} config Configuration options
42189  */
42190  
42191
42192 Roo.form.ComboBoxArray = function(config)
42193 {
42194     this.addEvents({
42195         /**
42196          * @event beforeremove
42197          * Fires before remove the value from the list
42198              * @param {Roo.form.ComboBoxArray} _self This combo box array
42199              * @param {Roo.form.ComboBoxArray.Item} item removed item
42200              */
42201         'beforeremove' : true,
42202         /**
42203          * @event remove
42204          * Fires when remove the value from the list
42205              * @param {Roo.form.ComboBoxArray} _self This combo box array
42206              * @param {Roo.form.ComboBoxArray.Item} item removed item
42207              */
42208         'remove' : true
42209         
42210         
42211     });
42212     
42213     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42214     
42215     this.items = new Roo.util.MixedCollection(false);
42216     
42217     // construct the child combo...
42218     
42219     
42220     
42221     
42222    
42223     
42224 }
42225
42226  
42227 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42228
42229     /**
42230      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42231      */
42232     
42233     lastData : false,
42234     
42235     // behavies liek a hiddne field
42236     inputType:      'hidden',
42237     /**
42238      * @cfg {Number} width The width of the box that displays the selected element
42239      */ 
42240     width:          300,
42241
42242     
42243     
42244     /**
42245      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42246      */
42247     name : false,
42248     /**
42249      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42250      */
42251     hiddenName : false,
42252     
42253     
42254     // private the array of items that are displayed..
42255     items  : false,
42256     // private - the hidden field el.
42257     hiddenEl : false,
42258     // private - the filed el..
42259     el : false,
42260     
42261     //validateValue : function() { return true; }, // all values are ok!
42262     //onAddClick: function() { },
42263     
42264     onRender : function(ct, position) 
42265     {
42266         
42267         // create the standard hidden element
42268         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42269         
42270         
42271         // give fake names to child combo;
42272         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42273         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42274         
42275         this.combo = Roo.factory(this.combo, Roo.form);
42276         this.combo.onRender(ct, position);
42277         if (typeof(this.combo.width) != 'undefined') {
42278             this.combo.onResize(this.combo.width,0);
42279         }
42280         
42281         this.combo.initEvents();
42282         
42283         // assigned so form know we need to do this..
42284         this.store          = this.combo.store;
42285         this.valueField     = this.combo.valueField;
42286         this.displayField   = this.combo.displayField ;
42287         
42288         
42289         this.combo.wrap.addClass('x-cbarray-grp');
42290         
42291         var cbwrap = this.combo.wrap.createChild(
42292             {tag: 'div', cls: 'x-cbarray-cb'},
42293             this.combo.el.dom
42294         );
42295         
42296              
42297         this.hiddenEl = this.combo.wrap.createChild({
42298             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42299         });
42300         this.el = this.combo.wrap.createChild({
42301             tag: 'input',  type:'hidden' , name: this.name, value : ''
42302         });
42303          //   this.el.dom.removeAttribute("name");
42304         
42305         
42306         this.outerWrap = this.combo.wrap;
42307         this.wrap = cbwrap;
42308         
42309         this.outerWrap.setWidth(this.width);
42310         this.outerWrap.dom.removeChild(this.el.dom);
42311         
42312         this.wrap.dom.appendChild(this.el.dom);
42313         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42314         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42315         
42316         this.combo.trigger.setStyle('position','relative');
42317         this.combo.trigger.setStyle('left', '0px');
42318         this.combo.trigger.setStyle('top', '2px');
42319         
42320         this.combo.el.setStyle('vertical-align', 'text-bottom');
42321         
42322         //this.trigger.setStyle('vertical-align', 'top');
42323         
42324         // this should use the code from combo really... on('add' ....)
42325         if (this.adder) {
42326             
42327         
42328             this.adder = this.outerWrap.createChild(
42329                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42330             var _t = this;
42331             this.adder.on('click', function(e) {
42332                 _t.fireEvent('adderclick', this, e);
42333             }, _t);
42334         }
42335         //var _t = this;
42336         //this.adder.on('click', this.onAddClick, _t);
42337         
42338         
42339         this.combo.on('select', function(cb, rec, ix) {
42340             this.addItem(rec.data);
42341             
42342             cb.setValue('');
42343             cb.el.dom.value = '';
42344             //cb.lastData = rec.data;
42345             // add to list
42346             
42347         }, this);
42348         
42349         
42350     },
42351     
42352     
42353     getName: function()
42354     {
42355         // returns hidden if it's set..
42356         if (!this.rendered) {return ''};
42357         return  this.hiddenName ? this.hiddenName : this.name;
42358         
42359     },
42360     
42361     
42362     onResize: function(w, h){
42363         
42364         return;
42365         // not sure if this is needed..
42366         //this.combo.onResize(w,h);
42367         
42368         if(typeof w != 'number'){
42369             // we do not handle it!?!?
42370             return;
42371         }
42372         var tw = this.combo.trigger.getWidth();
42373         tw += this.addicon ? this.addicon.getWidth() : 0;
42374         tw += this.editicon ? this.editicon.getWidth() : 0;
42375         var x = w - tw;
42376         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42377             
42378         this.combo.trigger.setStyle('left', '0px');
42379         
42380         if(this.list && this.listWidth === undefined){
42381             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42382             this.list.setWidth(lw);
42383             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42384         }
42385         
42386     
42387         
42388     },
42389     
42390     addItem: function(rec)
42391     {
42392         var valueField = this.combo.valueField;
42393         var displayField = this.combo.displayField;
42394         
42395         if (this.items.indexOfKey(rec[valueField]) > -1) {
42396             //console.log("GOT " + rec.data.id);
42397             return;
42398         }
42399         
42400         var x = new Roo.form.ComboBoxArray.Item({
42401             //id : rec[this.idField],
42402             data : rec,
42403             displayField : displayField ,
42404             tipField : displayField ,
42405             cb : this
42406         });
42407         // use the 
42408         this.items.add(rec[valueField],x);
42409         // add it before the element..
42410         this.updateHiddenEl();
42411         x.render(this.outerWrap, this.wrap.dom);
42412         // add the image handler..
42413     },
42414     
42415     updateHiddenEl : function()
42416     {
42417         this.validate();
42418         if (!this.hiddenEl) {
42419             return;
42420         }
42421         var ar = [];
42422         var idField = this.combo.valueField;
42423         
42424         this.items.each(function(f) {
42425             ar.push(f.data[idField]);
42426         });
42427         this.hiddenEl.dom.value = ar.join(',');
42428         this.validate();
42429     },
42430     
42431     reset : function()
42432     {
42433         this.items.clear();
42434         
42435         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42436            el.remove();
42437         });
42438         
42439         this.el.dom.value = '';
42440         if (this.hiddenEl) {
42441             this.hiddenEl.dom.value = '';
42442         }
42443         
42444     },
42445     getValue: function()
42446     {
42447         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42448     },
42449     setValue: function(v) // not a valid action - must use addItems..
42450     {
42451         
42452         this.reset();
42453          
42454         if (this.store.isLocal && (typeof(v) == 'string')) {
42455             // then we can use the store to find the values..
42456             // comma seperated at present.. this needs to allow JSON based encoding..
42457             this.hiddenEl.value  = v;
42458             var v_ar = [];
42459             Roo.each(v.split(','), function(k) {
42460                 Roo.log("CHECK " + this.valueField + ',' + k);
42461                 var li = this.store.query(this.valueField, k);
42462                 if (!li.length) {
42463                     return;
42464                 }
42465                 var add = {};
42466                 add[this.valueField] = k;
42467                 add[this.displayField] = li.item(0).data[this.displayField];
42468                 
42469                 this.addItem(add);
42470             }, this) 
42471              
42472         }
42473         if (typeof(v) == 'object' ) {
42474             // then let's assume it's an array of objects..
42475             Roo.each(v, function(l) {
42476                 this.addItem(l);
42477             }, this);
42478              
42479         }
42480         
42481         
42482     },
42483     setFromData: function(v)
42484     {
42485         // this recieves an object, if setValues is called.
42486         this.reset();
42487         this.el.dom.value = v[this.displayField];
42488         this.hiddenEl.dom.value = v[this.valueField];
42489         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42490             return;
42491         }
42492         var kv = v[this.valueField];
42493         var dv = v[this.displayField];
42494         kv = typeof(kv) != 'string' ? '' : kv;
42495         dv = typeof(dv) != 'string' ? '' : dv;
42496         
42497         
42498         var keys = kv.split(',');
42499         var display = dv.split(',');
42500         for (var i = 0 ; i < keys.length; i++) {
42501             
42502             add = {};
42503             add[this.valueField] = keys[i];
42504             add[this.displayField] = display[i];
42505             this.addItem(add);
42506         }
42507       
42508         
42509     },
42510     
42511     /**
42512      * Validates the combox array value
42513      * @return {Boolean} True if the value is valid, else false
42514      */
42515     validate : function(){
42516         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42517             this.clearInvalid();
42518             return true;
42519         }
42520         return false;
42521     },
42522     
42523     validateValue : function(value){
42524         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42525         
42526     },
42527     
42528     /*@
42529      * overide
42530      * 
42531      */
42532     isDirty : function() {
42533         if(this.disabled) {
42534             return false;
42535         }
42536         
42537         try {
42538             var d = Roo.decode(String(this.originalValue));
42539         } catch (e) {
42540             return String(this.getValue()) !== String(this.originalValue);
42541         }
42542         
42543         var originalValue = [];
42544         
42545         for (var i = 0; i < d.length; i++){
42546             originalValue.push(d[i][this.valueField]);
42547         }
42548         
42549         return String(this.getValue()) !== String(originalValue.join(','));
42550         
42551     }
42552     
42553 });
42554
42555
42556
42557 /**
42558  * @class Roo.form.ComboBoxArray.Item
42559  * @extends Roo.BoxComponent
42560  * A selected item in the list
42561  *  Fred [x]  Brian [x]  [Pick another |v]
42562  * 
42563  * @constructor
42564  * Create a new item.
42565  * @param {Object} config Configuration options
42566  */
42567  
42568 Roo.form.ComboBoxArray.Item = function(config) {
42569     config.id = Roo.id();
42570     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42571 }
42572
42573 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42574     data : {},
42575     cb: false,
42576     displayField : false,
42577     tipField : false,
42578     
42579     
42580     defaultAutoCreate : {
42581         tag: 'div',
42582         cls: 'x-cbarray-item',
42583         cn : [ 
42584             { tag: 'div' },
42585             {
42586                 tag: 'img',
42587                 width:16,
42588                 height : 16,
42589                 src : Roo.BLANK_IMAGE_URL ,
42590                 align: 'center'
42591             }
42592         ]
42593         
42594     },
42595     
42596  
42597     onRender : function(ct, position)
42598     {
42599         Roo.form.Field.superclass.onRender.call(this, ct, position);
42600         
42601         if(!this.el){
42602             var cfg = this.getAutoCreate();
42603             this.el = ct.createChild(cfg, position);
42604         }
42605         
42606         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42607         
42608         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42609             this.cb.renderer(this.data) :
42610             String.format('{0}',this.data[this.displayField]);
42611         
42612             
42613         this.el.child('div').dom.setAttribute('qtip',
42614                         String.format('{0}',this.data[this.tipField])
42615         );
42616         
42617         this.el.child('img').on('click', this.remove, this);
42618         
42619     },
42620    
42621     remove : function()
42622     {
42623         if(this.cb.disabled){
42624             return;
42625         }
42626         
42627         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42628             this.cb.items.remove(this);
42629             this.el.child('img').un('click', this.remove, this);
42630             this.el.remove();
42631             this.cb.updateHiddenEl();
42632
42633             this.cb.fireEvent('remove', this.cb, this);
42634         }
42635         
42636     }
42637 });/*
42638  * Based on:
42639  * Ext JS Library 1.1.1
42640  * Copyright(c) 2006-2007, Ext JS, LLC.
42641  *
42642  * Originally Released Under LGPL - original licence link has changed is not relivant.
42643  *
42644  * Fork - LGPL
42645  * <script type="text/javascript">
42646  */
42647 /**
42648  * @class Roo.form.Checkbox
42649  * @extends Roo.form.Field
42650  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42651  * @constructor
42652  * Creates a new Checkbox
42653  * @param {Object} config Configuration options
42654  */
42655 Roo.form.Checkbox = function(config){
42656     Roo.form.Checkbox.superclass.constructor.call(this, config);
42657     this.addEvents({
42658         /**
42659          * @event check
42660          * Fires when the checkbox is checked or unchecked.
42661              * @param {Roo.form.Checkbox} this This checkbox
42662              * @param {Boolean} checked The new checked value
42663              */
42664         check : true
42665     });
42666 };
42667
42668 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42669     /**
42670      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42671      */
42672     focusClass : undefined,
42673     /**
42674      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42675      */
42676     fieldClass: "x-form-field",
42677     /**
42678      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42679      */
42680     checked: false,
42681     /**
42682      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42683      * {tag: "input", type: "checkbox", autocomplete: "off"})
42684      */
42685     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42686     /**
42687      * @cfg {String} boxLabel The text that appears beside the checkbox
42688      */
42689     boxLabel : "",
42690     /**
42691      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42692      */  
42693     inputValue : '1',
42694     /**
42695      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42696      */
42697      valueOff: '0', // value when not checked..
42698
42699     actionMode : 'viewEl', 
42700     //
42701     // private
42702     itemCls : 'x-menu-check-item x-form-item',
42703     groupClass : 'x-menu-group-item',
42704     inputType : 'hidden',
42705     
42706     
42707     inSetChecked: false, // check that we are not calling self...
42708     
42709     inputElement: false, // real input element?
42710     basedOn: false, // ????
42711     
42712     isFormField: true, // not sure where this is needed!!!!
42713
42714     onResize : function(){
42715         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42716         if(!this.boxLabel){
42717             this.el.alignTo(this.wrap, 'c-c');
42718         }
42719     },
42720
42721     initEvents : function(){
42722         Roo.form.Checkbox.superclass.initEvents.call(this);
42723         this.el.on("click", this.onClick,  this);
42724         this.el.on("change", this.onClick,  this);
42725     },
42726
42727
42728     getResizeEl : function(){
42729         return this.wrap;
42730     },
42731
42732     getPositionEl : function(){
42733         return this.wrap;
42734     },
42735
42736     // private
42737     onRender : function(ct, position){
42738         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42739         /*
42740         if(this.inputValue !== undefined){
42741             this.el.dom.value = this.inputValue;
42742         }
42743         */
42744         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42745         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42746         var viewEl = this.wrap.createChild({ 
42747             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42748         this.viewEl = viewEl;   
42749         this.wrap.on('click', this.onClick,  this); 
42750         
42751         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42752         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42753         
42754         
42755         
42756         if(this.boxLabel){
42757             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42758         //    viewEl.on('click', this.onClick,  this); 
42759         }
42760         //if(this.checked){
42761             this.setChecked(this.checked);
42762         //}else{
42763             //this.checked = this.el.dom;
42764         //}
42765
42766     },
42767
42768     // private
42769     initValue : Roo.emptyFn,
42770
42771     /**
42772      * Returns the checked state of the checkbox.
42773      * @return {Boolean} True if checked, else false
42774      */
42775     getValue : function(){
42776         if(this.el){
42777             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42778         }
42779         return this.valueOff;
42780         
42781     },
42782
42783         // private
42784     onClick : function(){ 
42785         if (this.disabled) {
42786             return;
42787         }
42788         this.setChecked(!this.checked);
42789
42790         //if(this.el.dom.checked != this.checked){
42791         //    this.setValue(this.el.dom.checked);
42792        // }
42793     },
42794
42795     /**
42796      * Sets the checked state of the checkbox.
42797      * On is always based on a string comparison between inputValue and the param.
42798      * @param {Boolean/String} value - the value to set 
42799      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42800      */
42801     setValue : function(v,suppressEvent){
42802         
42803         
42804         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42805         //if(this.el && this.el.dom){
42806         //    this.el.dom.checked = this.checked;
42807         //    this.el.dom.defaultChecked = this.checked;
42808         //}
42809         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42810         //this.fireEvent("check", this, this.checked);
42811     },
42812     // private..
42813     setChecked : function(state,suppressEvent)
42814     {
42815         if (this.inSetChecked) {
42816             this.checked = state;
42817             return;
42818         }
42819         
42820     
42821         if(this.wrap){
42822             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42823         }
42824         this.checked = state;
42825         if(suppressEvent !== true){
42826             this.fireEvent('check', this, state);
42827         }
42828         this.inSetChecked = true;
42829         this.el.dom.value = state ? this.inputValue : this.valueOff;
42830         this.inSetChecked = false;
42831         
42832     },
42833     // handle setting of hidden value by some other method!!?!?
42834     setFromHidden: function()
42835     {
42836         if(!this.el){
42837             return;
42838         }
42839         //console.log("SET FROM HIDDEN");
42840         //alert('setFrom hidden');
42841         this.setValue(this.el.dom.value);
42842     },
42843     
42844     onDestroy : function()
42845     {
42846         if(this.viewEl){
42847             Roo.get(this.viewEl).remove();
42848         }
42849          
42850         Roo.form.Checkbox.superclass.onDestroy.call(this);
42851     },
42852     
42853     setBoxLabel : function(str)
42854     {
42855         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42856     }
42857
42858 });/*
42859  * Based on:
42860  * Ext JS Library 1.1.1
42861  * Copyright(c) 2006-2007, Ext JS, LLC.
42862  *
42863  * Originally Released Under LGPL - original licence link has changed is not relivant.
42864  *
42865  * Fork - LGPL
42866  * <script type="text/javascript">
42867  */
42868  
42869 /**
42870  * @class Roo.form.Radio
42871  * @extends Roo.form.Checkbox
42872  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42873  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42874  * @constructor
42875  * Creates a new Radio
42876  * @param {Object} config Configuration options
42877  */
42878 Roo.form.Radio = function(){
42879     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42880 };
42881 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42882     inputType: 'radio',
42883
42884     /**
42885      * If this radio is part of a group, it will return the selected value
42886      * @return {String}
42887      */
42888     getGroupValue : function(){
42889         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42890     },
42891     
42892     
42893     onRender : function(ct, position){
42894         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42895         
42896         if(this.inputValue !== undefined){
42897             this.el.dom.value = this.inputValue;
42898         }
42899          
42900         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42901         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42902         //var viewEl = this.wrap.createChild({ 
42903         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42904         //this.viewEl = viewEl;   
42905         //this.wrap.on('click', this.onClick,  this); 
42906         
42907         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42908         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42909         
42910         
42911         
42912         if(this.boxLabel){
42913             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42914         //    viewEl.on('click', this.onClick,  this); 
42915         }
42916          if(this.checked){
42917             this.el.dom.checked =   'checked' ;
42918         }
42919          
42920     } 
42921     
42922     
42923 });//<script type="text/javascript">
42924
42925 /*
42926  * Based  Ext JS Library 1.1.1
42927  * Copyright(c) 2006-2007, Ext JS, LLC.
42928  * LGPL
42929  *
42930  */
42931  
42932 /**
42933  * @class Roo.HtmlEditorCore
42934  * @extends Roo.Component
42935  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42936  *
42937  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42938  */
42939
42940 Roo.HtmlEditorCore = function(config){
42941     
42942     
42943     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42944     
42945     
42946     this.addEvents({
42947         /**
42948          * @event initialize
42949          * Fires when the editor is fully initialized (including the iframe)
42950          * @param {Roo.HtmlEditorCore} this
42951          */
42952         initialize: true,
42953         /**
42954          * @event activate
42955          * Fires when the editor is first receives the focus. Any insertion must wait
42956          * until after this event.
42957          * @param {Roo.HtmlEditorCore} this
42958          */
42959         activate: true,
42960          /**
42961          * @event beforesync
42962          * Fires before the textarea is updated with content from the editor iframe. Return false
42963          * to cancel the sync.
42964          * @param {Roo.HtmlEditorCore} this
42965          * @param {String} html
42966          */
42967         beforesync: true,
42968          /**
42969          * @event beforepush
42970          * Fires before the iframe editor is updated with content from the textarea. Return false
42971          * to cancel the push.
42972          * @param {Roo.HtmlEditorCore} this
42973          * @param {String} html
42974          */
42975         beforepush: true,
42976          /**
42977          * @event sync
42978          * Fires when the textarea is updated with content from the editor iframe.
42979          * @param {Roo.HtmlEditorCore} this
42980          * @param {String} html
42981          */
42982         sync: true,
42983          /**
42984          * @event push
42985          * Fires when the iframe editor is updated with content from the textarea.
42986          * @param {Roo.HtmlEditorCore} this
42987          * @param {String} html
42988          */
42989         push: true,
42990         
42991         /**
42992          * @event editorevent
42993          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42994          * @param {Roo.HtmlEditorCore} this
42995          */
42996         editorevent: true
42997         
42998     });
42999     
43000     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43001     
43002     // defaults : white / black...
43003     this.applyBlacklists();
43004     
43005     
43006     
43007 };
43008
43009
43010 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43011
43012
43013      /**
43014      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43015      */
43016     
43017     owner : false,
43018     
43019      /**
43020      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43021      *                        Roo.resizable.
43022      */
43023     resizable : false,
43024      /**
43025      * @cfg {Number} height (in pixels)
43026      */   
43027     height: 300,
43028    /**
43029      * @cfg {Number} width (in pixels)
43030      */   
43031     width: 500,
43032     
43033     /**
43034      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43035      * 
43036      */
43037     stylesheets: false,
43038     
43039     // id of frame..
43040     frameId: false,
43041     
43042     // private properties
43043     validationEvent : false,
43044     deferHeight: true,
43045     initialized : false,
43046     activated : false,
43047     sourceEditMode : false,
43048     onFocus : Roo.emptyFn,
43049     iframePad:3,
43050     hideMode:'offsets',
43051     
43052     clearUp: true,
43053     
43054     // blacklist + whitelisted elements..
43055     black: false,
43056     white: false,
43057      
43058     bodyCls : '',
43059
43060     /**
43061      * Protected method that will not generally be called directly. It
43062      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43063      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43064      */
43065     getDocMarkup : function(){
43066         // body styles..
43067         var st = '';
43068         
43069         // inherit styels from page...?? 
43070         if (this.stylesheets === false) {
43071             
43072             Roo.get(document.head).select('style').each(function(node) {
43073                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43074             });
43075             
43076             Roo.get(document.head).select('link').each(function(node) { 
43077                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43078             });
43079             
43080         } else if (!this.stylesheets.length) {
43081                 // simple..
43082                 st = '<style type="text/css">' +
43083                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43084                    '</style>';
43085         } else { 
43086             st = '<style type="text/css">' +
43087                     this.stylesheets +
43088                 '</style>';
43089         }
43090         
43091         st +=  '<style type="text/css">' +
43092             'IMG { cursor: pointer } ' +
43093         '</style>';
43094
43095         var cls = 'roo-htmleditor-body';
43096         
43097         if(this.bodyCls.length){
43098             cls += ' ' + this.bodyCls;
43099         }
43100         
43101         return '<html><head>' + st  +
43102             //<style type="text/css">' +
43103             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43104             //'</style>' +
43105             ' </head><body class="' +  cls + '"></body></html>';
43106     },
43107
43108     // private
43109     onRender : function(ct, position)
43110     {
43111         var _t = this;
43112         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43113         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43114         
43115         
43116         this.el.dom.style.border = '0 none';
43117         this.el.dom.setAttribute('tabIndex', -1);
43118         this.el.addClass('x-hidden hide');
43119         
43120         
43121         
43122         if(Roo.isIE){ // fix IE 1px bogus margin
43123             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43124         }
43125        
43126         
43127         this.frameId = Roo.id();
43128         
43129          
43130         
43131         var iframe = this.owner.wrap.createChild({
43132             tag: 'iframe',
43133             cls: 'form-control', // bootstrap..
43134             id: this.frameId,
43135             name: this.frameId,
43136             frameBorder : 'no',
43137             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43138         }, this.el
43139         );
43140         
43141         
43142         this.iframe = iframe.dom;
43143
43144          this.assignDocWin();
43145         
43146         this.doc.designMode = 'on';
43147        
43148         this.doc.open();
43149         this.doc.write(this.getDocMarkup());
43150         this.doc.close();
43151
43152         
43153         var task = { // must defer to wait for browser to be ready
43154             run : function(){
43155                 //console.log("run task?" + this.doc.readyState);
43156                 this.assignDocWin();
43157                 if(this.doc.body || this.doc.readyState == 'complete'){
43158                     try {
43159                         this.doc.designMode="on";
43160                     } catch (e) {
43161                         return;
43162                     }
43163                     Roo.TaskMgr.stop(task);
43164                     this.initEditor.defer(10, this);
43165                 }
43166             },
43167             interval : 10,
43168             duration: 10000,
43169             scope: this
43170         };
43171         Roo.TaskMgr.start(task);
43172
43173     },
43174
43175     // private
43176     onResize : function(w, h)
43177     {
43178          Roo.log('resize: ' +w + ',' + h );
43179         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43180         if(!this.iframe){
43181             return;
43182         }
43183         if(typeof w == 'number'){
43184             
43185             this.iframe.style.width = w + 'px';
43186         }
43187         if(typeof h == 'number'){
43188             
43189             this.iframe.style.height = h + 'px';
43190             if(this.doc){
43191                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43192             }
43193         }
43194         
43195     },
43196
43197     /**
43198      * Toggles the editor between standard and source edit mode.
43199      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43200      */
43201     toggleSourceEdit : function(sourceEditMode){
43202         
43203         this.sourceEditMode = sourceEditMode === true;
43204         
43205         if(this.sourceEditMode){
43206  
43207             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43208             
43209         }else{
43210             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43211             //this.iframe.className = '';
43212             this.deferFocus();
43213         }
43214         //this.setSize(this.owner.wrap.getSize());
43215         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43216     },
43217
43218     
43219   
43220
43221     /**
43222      * Protected method that will not generally be called directly. If you need/want
43223      * custom HTML cleanup, this is the method you should override.
43224      * @param {String} html The HTML to be cleaned
43225      * return {String} The cleaned HTML
43226      */
43227     cleanHtml : function(html){
43228         html = String(html);
43229         if(html.length > 5){
43230             if(Roo.isSafari){ // strip safari nonsense
43231                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43232             }
43233         }
43234         if(html == '&nbsp;'){
43235             html = '';
43236         }
43237         return html;
43238     },
43239
43240     /**
43241      * HTML Editor -> Textarea
43242      * Protected method that will not generally be called directly. Syncs the contents
43243      * of the editor iframe with the textarea.
43244      */
43245     syncValue : function(){
43246         if(this.initialized){
43247             var bd = (this.doc.body || this.doc.documentElement);
43248             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43249             var html = bd.innerHTML;
43250             if(Roo.isSafari){
43251                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43252                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43253                 if(m && m[1]){
43254                     html = '<div style="'+m[0]+'">' + html + '</div>';
43255                 }
43256             }
43257             html = this.cleanHtml(html);
43258             // fix up the special chars.. normaly like back quotes in word...
43259             // however we do not want to do this with chinese..
43260             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43261                 
43262                 var cc = match.charCodeAt();
43263
43264                 // Get the character value, handling surrogate pairs
43265                 if (match.length == 2) {
43266                     // It's a surrogate pair, calculate the Unicode code point
43267                     var high = match.charCodeAt(0) - 0xD800;
43268                     var low  = match.charCodeAt(1) - 0xDC00;
43269                     cc = (high * 0x400) + low + 0x10000;
43270                 }  else if (
43271                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43272                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43273                     (cc >= 0xf900 && cc < 0xfb00 )
43274                 ) {
43275                         return match;
43276                 }  
43277          
43278                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43279                 return "&#" + cc + ";";
43280                 
43281                 
43282             });
43283             
43284             
43285              
43286             if(this.owner.fireEvent('beforesync', this, html) !== false){
43287                 this.el.dom.value = html;
43288                 this.owner.fireEvent('sync', this, html);
43289             }
43290         }
43291     },
43292
43293     /**
43294      * Protected method that will not generally be called directly. Pushes the value of the textarea
43295      * into the iframe editor.
43296      */
43297     pushValue : function(){
43298         if(this.initialized){
43299             var v = this.el.dom.value.trim();
43300             
43301 //            if(v.length < 1){
43302 //                v = '&#160;';
43303 //            }
43304             
43305             if(this.owner.fireEvent('beforepush', this, v) !== false){
43306                 var d = (this.doc.body || this.doc.documentElement);
43307                 d.innerHTML = v;
43308                 this.cleanUpPaste();
43309                 this.el.dom.value = d.innerHTML;
43310                 this.owner.fireEvent('push', this, v);
43311             }
43312         }
43313     },
43314
43315     // private
43316     deferFocus : function(){
43317         this.focus.defer(10, this);
43318     },
43319
43320     // doc'ed in Field
43321     focus : function(){
43322         if(this.win && !this.sourceEditMode){
43323             this.win.focus();
43324         }else{
43325             this.el.focus();
43326         }
43327     },
43328     
43329     assignDocWin: function()
43330     {
43331         var iframe = this.iframe;
43332         
43333          if(Roo.isIE){
43334             this.doc = iframe.contentWindow.document;
43335             this.win = iframe.contentWindow;
43336         } else {
43337 //            if (!Roo.get(this.frameId)) {
43338 //                return;
43339 //            }
43340 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43341 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43342             
43343             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43344                 return;
43345             }
43346             
43347             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43348             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43349         }
43350     },
43351     
43352     // private
43353     initEditor : function(){
43354         //console.log("INIT EDITOR");
43355         this.assignDocWin();
43356         
43357         
43358         
43359         this.doc.designMode="on";
43360         this.doc.open();
43361         this.doc.write(this.getDocMarkup());
43362         this.doc.close();
43363         
43364         var dbody = (this.doc.body || this.doc.documentElement);
43365         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43366         // this copies styles from the containing element into thsi one..
43367         // not sure why we need all of this..
43368         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43369         
43370         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43371         //ss['background-attachment'] = 'fixed'; // w3c
43372         dbody.bgProperties = 'fixed'; // ie
43373         //Roo.DomHelper.applyStyles(dbody, ss);
43374         Roo.EventManager.on(this.doc, {
43375             //'mousedown': this.onEditorEvent,
43376             'mouseup': this.onEditorEvent,
43377             'dblclick': this.onEditorEvent,
43378             'click': this.onEditorEvent,
43379             'keyup': this.onEditorEvent,
43380             buffer:100,
43381             scope: this
43382         });
43383         if(Roo.isGecko){
43384             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43385         }
43386         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43387             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43388         }
43389         this.initialized = true;
43390
43391         this.owner.fireEvent('initialize', this);
43392         this.pushValue();
43393     },
43394
43395     // private
43396     onDestroy : function(){
43397         
43398         
43399         
43400         if(this.rendered){
43401             
43402             //for (var i =0; i < this.toolbars.length;i++) {
43403             //    // fixme - ask toolbars for heights?
43404             //    this.toolbars[i].onDestroy();
43405            // }
43406             
43407             //this.wrap.dom.innerHTML = '';
43408             //this.wrap.remove();
43409         }
43410     },
43411
43412     // private
43413     onFirstFocus : function(){
43414         
43415         this.assignDocWin();
43416         
43417         
43418         this.activated = true;
43419          
43420     
43421         if(Roo.isGecko){ // prevent silly gecko errors
43422             this.win.focus();
43423             var s = this.win.getSelection();
43424             if(!s.focusNode || s.focusNode.nodeType != 3){
43425                 var r = s.getRangeAt(0);
43426                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43427                 r.collapse(true);
43428                 this.deferFocus();
43429             }
43430             try{
43431                 this.execCmd('useCSS', true);
43432                 this.execCmd('styleWithCSS', false);
43433             }catch(e){}
43434         }
43435         this.owner.fireEvent('activate', this);
43436     },
43437
43438     // private
43439     adjustFont: function(btn){
43440         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43441         //if(Roo.isSafari){ // safari
43442         //    adjust *= 2;
43443        // }
43444         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43445         if(Roo.isSafari){ // safari
43446             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43447             v =  (v < 10) ? 10 : v;
43448             v =  (v > 48) ? 48 : v;
43449             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43450             
43451         }
43452         
43453         
43454         v = Math.max(1, v+adjust);
43455         
43456         this.execCmd('FontSize', v  );
43457     },
43458
43459     onEditorEvent : function(e)
43460     {
43461         this.owner.fireEvent('editorevent', this, e);
43462       //  this.updateToolbar();
43463         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43464     },
43465
43466     insertTag : function(tg)
43467     {
43468         // could be a bit smarter... -> wrap the current selected tRoo..
43469         if (tg.toLowerCase() == 'span' ||
43470             tg.toLowerCase() == 'code' ||
43471             tg.toLowerCase() == 'sup' ||
43472             tg.toLowerCase() == 'sub' 
43473             ) {
43474             
43475             range = this.createRange(this.getSelection());
43476             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43477             wrappingNode.appendChild(range.extractContents());
43478             range.insertNode(wrappingNode);
43479
43480             return;
43481             
43482             
43483             
43484         }
43485         this.execCmd("formatblock",   tg);
43486         
43487     },
43488     
43489     insertText : function(txt)
43490     {
43491         
43492         
43493         var range = this.createRange();
43494         range.deleteContents();
43495                //alert(Sender.getAttribute('label'));
43496                
43497         range.insertNode(this.doc.createTextNode(txt));
43498     } ,
43499     
43500      
43501
43502     /**
43503      * Executes a Midas editor command on the editor document and performs necessary focus and
43504      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43505      * @param {String} cmd The Midas command
43506      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43507      */
43508     relayCmd : function(cmd, value){
43509         this.win.focus();
43510         this.execCmd(cmd, value);
43511         this.owner.fireEvent('editorevent', this);
43512         //this.updateToolbar();
43513         this.owner.deferFocus();
43514     },
43515
43516     /**
43517      * Executes a Midas editor command directly on the editor document.
43518      * For visual commands, you should use {@link #relayCmd} instead.
43519      * <b>This should only be called after the editor is initialized.</b>
43520      * @param {String} cmd The Midas command
43521      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43522      */
43523     execCmd : function(cmd, value){
43524         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43525         this.syncValue();
43526     },
43527  
43528  
43529    
43530     /**
43531      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43532      * to insert tRoo.
43533      * @param {String} text | dom node.. 
43534      */
43535     insertAtCursor : function(text)
43536     {
43537         
43538         if(!this.activated){
43539             return;
43540         }
43541         /*
43542         if(Roo.isIE){
43543             this.win.focus();
43544             var r = this.doc.selection.createRange();
43545             if(r){
43546                 r.collapse(true);
43547                 r.pasteHTML(text);
43548                 this.syncValue();
43549                 this.deferFocus();
43550             
43551             }
43552             return;
43553         }
43554         */
43555         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43556             this.win.focus();
43557             
43558             
43559             // from jquery ui (MIT licenced)
43560             var range, node;
43561             var win = this.win;
43562             
43563             if (win.getSelection && win.getSelection().getRangeAt) {
43564                 range = win.getSelection().getRangeAt(0);
43565                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43566                 range.insertNode(node);
43567             } else if (win.document.selection && win.document.selection.createRange) {
43568                 // no firefox support
43569                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43570                 win.document.selection.createRange().pasteHTML(txt);
43571             } else {
43572                 // no firefox support
43573                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43574                 this.execCmd('InsertHTML', txt);
43575             } 
43576             
43577             this.syncValue();
43578             
43579             this.deferFocus();
43580         }
43581     },
43582  // private
43583     mozKeyPress : function(e){
43584         if(e.ctrlKey){
43585             var c = e.getCharCode(), cmd;
43586           
43587             if(c > 0){
43588                 c = String.fromCharCode(c).toLowerCase();
43589                 switch(c){
43590                     case 'b':
43591                         cmd = 'bold';
43592                         break;
43593                     case 'i':
43594                         cmd = 'italic';
43595                         break;
43596                     
43597                     case 'u':
43598                         cmd = 'underline';
43599                         break;
43600                     
43601                     case 'v':
43602                         this.cleanUpPaste.defer(100, this);
43603                         return;
43604                         
43605                 }
43606                 if(cmd){
43607                     this.win.focus();
43608                     this.execCmd(cmd);
43609                     this.deferFocus();
43610                     e.preventDefault();
43611                 }
43612                 
43613             }
43614         }
43615     },
43616
43617     // private
43618     fixKeys : function(){ // load time branching for fastest keydown performance
43619         if(Roo.isIE){
43620             return function(e){
43621                 var k = e.getKey(), r;
43622                 if(k == e.TAB){
43623                     e.stopEvent();
43624                     r = this.doc.selection.createRange();
43625                     if(r){
43626                         r.collapse(true);
43627                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43628                         this.deferFocus();
43629                     }
43630                     return;
43631                 }
43632                 
43633                 if(k == e.ENTER){
43634                     r = this.doc.selection.createRange();
43635                     if(r){
43636                         var target = r.parentElement();
43637                         if(!target || target.tagName.toLowerCase() != 'li'){
43638                             e.stopEvent();
43639                             r.pasteHTML('<br />');
43640                             r.collapse(false);
43641                             r.select();
43642                         }
43643                     }
43644                 }
43645                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43646                     this.cleanUpPaste.defer(100, this);
43647                     return;
43648                 }
43649                 
43650                 
43651             };
43652         }else if(Roo.isOpera){
43653             return function(e){
43654                 var k = e.getKey();
43655                 if(k == e.TAB){
43656                     e.stopEvent();
43657                     this.win.focus();
43658                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43659                     this.deferFocus();
43660                 }
43661                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43662                     this.cleanUpPaste.defer(100, this);
43663                     return;
43664                 }
43665                 
43666             };
43667         }else if(Roo.isSafari){
43668             return function(e){
43669                 var k = e.getKey();
43670                 
43671                 if(k == e.TAB){
43672                     e.stopEvent();
43673                     this.execCmd('InsertText','\t');
43674                     this.deferFocus();
43675                     return;
43676                 }
43677                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43678                     this.cleanUpPaste.defer(100, this);
43679                     return;
43680                 }
43681                 
43682              };
43683         }
43684     }(),
43685     
43686     getAllAncestors: function()
43687     {
43688         var p = this.getSelectedNode();
43689         var a = [];
43690         if (!p) {
43691             a.push(p); // push blank onto stack..
43692             p = this.getParentElement();
43693         }
43694         
43695         
43696         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43697             a.push(p);
43698             p = p.parentNode;
43699         }
43700         a.push(this.doc.body);
43701         return a;
43702     },
43703     lastSel : false,
43704     lastSelNode : false,
43705     
43706     
43707     getSelection : function() 
43708     {
43709         this.assignDocWin();
43710         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43711     },
43712     
43713     getSelectedNode: function() 
43714     {
43715         // this may only work on Gecko!!!
43716         
43717         // should we cache this!!!!
43718         
43719         
43720         
43721          
43722         var range = this.createRange(this.getSelection()).cloneRange();
43723         
43724         if (Roo.isIE) {
43725             var parent = range.parentElement();
43726             while (true) {
43727                 var testRange = range.duplicate();
43728                 testRange.moveToElementText(parent);
43729                 if (testRange.inRange(range)) {
43730                     break;
43731                 }
43732                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43733                     break;
43734                 }
43735                 parent = parent.parentElement;
43736             }
43737             return parent;
43738         }
43739         
43740         // is ancestor a text element.
43741         var ac =  range.commonAncestorContainer;
43742         if (ac.nodeType == 3) {
43743             ac = ac.parentNode;
43744         }
43745         
43746         var ar = ac.childNodes;
43747          
43748         var nodes = [];
43749         var other_nodes = [];
43750         var has_other_nodes = false;
43751         for (var i=0;i<ar.length;i++) {
43752             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43753                 continue;
43754             }
43755             // fullly contained node.
43756             
43757             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43758                 nodes.push(ar[i]);
43759                 continue;
43760             }
43761             
43762             // probably selected..
43763             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43764                 other_nodes.push(ar[i]);
43765                 continue;
43766             }
43767             // outer..
43768             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43769                 continue;
43770             }
43771             
43772             
43773             has_other_nodes = true;
43774         }
43775         if (!nodes.length && other_nodes.length) {
43776             nodes= other_nodes;
43777         }
43778         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43779             return false;
43780         }
43781         
43782         return nodes[0];
43783     },
43784     createRange: function(sel)
43785     {
43786         // this has strange effects when using with 
43787         // top toolbar - not sure if it's a great idea.
43788         //this.editor.contentWindow.focus();
43789         if (typeof sel != "undefined") {
43790             try {
43791                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43792             } catch(e) {
43793                 return this.doc.createRange();
43794             }
43795         } else {
43796             return this.doc.createRange();
43797         }
43798     },
43799     getParentElement: function()
43800     {
43801         
43802         this.assignDocWin();
43803         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43804         
43805         var range = this.createRange(sel);
43806          
43807         try {
43808             var p = range.commonAncestorContainer;
43809             while (p.nodeType == 3) { // text node
43810                 p = p.parentNode;
43811             }
43812             return p;
43813         } catch (e) {
43814             return null;
43815         }
43816     
43817     },
43818     /***
43819      *
43820      * Range intersection.. the hard stuff...
43821      *  '-1' = before
43822      *  '0' = hits..
43823      *  '1' = after.
43824      *         [ -- selected range --- ]
43825      *   [fail]                        [fail]
43826      *
43827      *    basically..
43828      *      if end is before start or  hits it. fail.
43829      *      if start is after end or hits it fail.
43830      *
43831      *   if either hits (but other is outside. - then it's not 
43832      *   
43833      *    
43834      **/
43835     
43836     
43837     // @see http://www.thismuchiknow.co.uk/?p=64.
43838     rangeIntersectsNode : function(range, node)
43839     {
43840         var nodeRange = node.ownerDocument.createRange();
43841         try {
43842             nodeRange.selectNode(node);
43843         } catch (e) {
43844             nodeRange.selectNodeContents(node);
43845         }
43846     
43847         var rangeStartRange = range.cloneRange();
43848         rangeStartRange.collapse(true);
43849     
43850         var rangeEndRange = range.cloneRange();
43851         rangeEndRange.collapse(false);
43852     
43853         var nodeStartRange = nodeRange.cloneRange();
43854         nodeStartRange.collapse(true);
43855     
43856         var nodeEndRange = nodeRange.cloneRange();
43857         nodeEndRange.collapse(false);
43858     
43859         return rangeStartRange.compareBoundaryPoints(
43860                  Range.START_TO_START, nodeEndRange) == -1 &&
43861                rangeEndRange.compareBoundaryPoints(
43862                  Range.START_TO_START, nodeStartRange) == 1;
43863         
43864          
43865     },
43866     rangeCompareNode : function(range, node)
43867     {
43868         var nodeRange = node.ownerDocument.createRange();
43869         try {
43870             nodeRange.selectNode(node);
43871         } catch (e) {
43872             nodeRange.selectNodeContents(node);
43873         }
43874         
43875         
43876         range.collapse(true);
43877     
43878         nodeRange.collapse(true);
43879      
43880         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43881         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43882          
43883         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43884         
43885         var nodeIsBefore   =  ss == 1;
43886         var nodeIsAfter    = ee == -1;
43887         
43888         if (nodeIsBefore && nodeIsAfter) {
43889             return 0; // outer
43890         }
43891         if (!nodeIsBefore && nodeIsAfter) {
43892             return 1; //right trailed.
43893         }
43894         
43895         if (nodeIsBefore && !nodeIsAfter) {
43896             return 2;  // left trailed.
43897         }
43898         // fully contined.
43899         return 3;
43900     },
43901
43902     // private? - in a new class?
43903     cleanUpPaste :  function()
43904     {
43905         // cleans up the whole document..
43906         Roo.log('cleanuppaste');
43907         
43908         this.cleanUpChildren(this.doc.body);
43909         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43910         if (clean != this.doc.body.innerHTML) {
43911             this.doc.body.innerHTML = clean;
43912         }
43913         
43914     },
43915     
43916     cleanWordChars : function(input) {// change the chars to hex code
43917         var he = Roo.HtmlEditorCore;
43918         
43919         var output = input;
43920         Roo.each(he.swapCodes, function(sw) { 
43921             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43922             
43923             output = output.replace(swapper, sw[1]);
43924         });
43925         
43926         return output;
43927     },
43928     
43929     
43930     cleanUpChildren : function (n)
43931     {
43932         if (!n.childNodes.length) {
43933             return;
43934         }
43935         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43936            this.cleanUpChild(n.childNodes[i]);
43937         }
43938     },
43939     
43940     
43941         
43942     
43943     cleanUpChild : function (node)
43944     {
43945         var ed = this;
43946         //console.log(node);
43947         if (node.nodeName == "#text") {
43948             // clean up silly Windows -- stuff?
43949             return; 
43950         }
43951         if (node.nodeName == "#comment") {
43952             node.parentNode.removeChild(node);
43953             // clean up silly Windows -- stuff?
43954             return; 
43955         }
43956         var lcname = node.tagName.toLowerCase();
43957         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43958         // whitelist of tags..
43959         
43960         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43961             // remove node.
43962             node.parentNode.removeChild(node);
43963             return;
43964             
43965         }
43966         
43967         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43968         
43969         // spans with no attributes - just remove them..
43970         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
43971             remove_keep_children = true;
43972         }
43973         
43974         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43975         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43976         
43977         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43978         //    remove_keep_children = true;
43979         //}
43980         
43981         if (remove_keep_children) {
43982             this.cleanUpChildren(node);
43983             // inserts everything just before this node...
43984             while (node.childNodes.length) {
43985                 var cn = node.childNodes[0];
43986                 node.removeChild(cn);
43987                 node.parentNode.insertBefore(cn, node);
43988             }
43989             node.parentNode.removeChild(node);
43990             return;
43991         }
43992         
43993         if (!node.attributes || !node.attributes.length) {
43994             
43995           
43996             
43997             
43998             this.cleanUpChildren(node);
43999             return;
44000         }
44001         
44002         function cleanAttr(n,v)
44003         {
44004             
44005             if (v.match(/^\./) || v.match(/^\//)) {
44006                 return;
44007             }
44008             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44009                 return;
44010             }
44011             if (v.match(/^#/)) {
44012                 return;
44013             }
44014 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44015             node.removeAttribute(n);
44016             
44017         }
44018         
44019         var cwhite = this.cwhite;
44020         var cblack = this.cblack;
44021             
44022         function cleanStyle(n,v)
44023         {
44024             if (v.match(/expression/)) { //XSS?? should we even bother..
44025                 node.removeAttribute(n);
44026                 return;
44027             }
44028             
44029             var parts = v.split(/;/);
44030             var clean = [];
44031             
44032             Roo.each(parts, function(p) {
44033                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44034                 if (!p.length) {
44035                     return true;
44036                 }
44037                 var l = p.split(':').shift().replace(/\s+/g,'');
44038                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44039                 
44040                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44041 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44042                     //node.removeAttribute(n);
44043                     return true;
44044                 }
44045                 //Roo.log()
44046                 // only allow 'c whitelisted system attributes'
44047                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44048 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44049                     //node.removeAttribute(n);
44050                     return true;
44051                 }
44052                 
44053                 
44054                  
44055                 
44056                 clean.push(p);
44057                 return true;
44058             });
44059             if (clean.length) { 
44060                 node.setAttribute(n, clean.join(';'));
44061             } else {
44062                 node.removeAttribute(n);
44063             }
44064             
44065         }
44066         
44067         
44068         for (var i = node.attributes.length-1; i > -1 ; i--) {
44069             var a = node.attributes[i];
44070             //console.log(a);
44071             
44072             if (a.name.toLowerCase().substr(0,2)=='on')  {
44073                 node.removeAttribute(a.name);
44074                 continue;
44075             }
44076             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44077                 node.removeAttribute(a.name);
44078                 continue;
44079             }
44080             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44081                 cleanAttr(a.name,a.value); // fixme..
44082                 continue;
44083             }
44084             if (a.name == 'style') {
44085                 cleanStyle(a.name,a.value);
44086                 continue;
44087             }
44088             /// clean up MS crap..
44089             // tecnically this should be a list of valid class'es..
44090             
44091             
44092             if (a.name == 'class') {
44093                 if (a.value.match(/^Mso/)) {
44094                     node.removeAttribute('class');
44095                 }
44096                 
44097                 if (a.value.match(/^body$/)) {
44098                     node.removeAttribute('class');
44099                 }
44100                 continue;
44101             }
44102             
44103             // style cleanup!?
44104             // class cleanup?
44105             
44106         }
44107         
44108         
44109         this.cleanUpChildren(node);
44110         
44111         
44112     },
44113     
44114     /**
44115      * Clean up MS wordisms...
44116      */
44117     cleanWord : function(node)
44118     {
44119         if (!node) {
44120             this.cleanWord(this.doc.body);
44121             return;
44122         }
44123         
44124         if(
44125                 node.nodeName == 'SPAN' &&
44126                 !node.hasAttributes() &&
44127                 node.childNodes.length == 1 &&
44128                 node.firstChild.nodeName == "#text"  
44129         ) {
44130             var textNode = node.firstChild;
44131             node.removeChild(textNode);
44132             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44133                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44134             }
44135             node.parentNode.insertBefore(textNode, node);
44136             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44137                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44138             }
44139             node.parentNode.removeChild(node);
44140         }
44141         
44142         if (node.nodeName == "#text") {
44143             // clean up silly Windows -- stuff?
44144             return; 
44145         }
44146         if (node.nodeName == "#comment") {
44147             node.parentNode.removeChild(node);
44148             // clean up silly Windows -- stuff?
44149             return; 
44150         }
44151         
44152         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44153             node.parentNode.removeChild(node);
44154             return;
44155         }
44156         //Roo.log(node.tagName);
44157         // remove - but keep children..
44158         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44159             //Roo.log('-- removed');
44160             while (node.childNodes.length) {
44161                 var cn = node.childNodes[0];
44162                 node.removeChild(cn);
44163                 node.parentNode.insertBefore(cn, node);
44164                 // move node to parent - and clean it..
44165                 this.cleanWord(cn);
44166             }
44167             node.parentNode.removeChild(node);
44168             /// no need to iterate chidlren = it's got none..
44169             //this.iterateChildren(node, this.cleanWord);
44170             return;
44171         }
44172         // clean styles
44173         if (node.className.length) {
44174             
44175             var cn = node.className.split(/\W+/);
44176             var cna = [];
44177             Roo.each(cn, function(cls) {
44178                 if (cls.match(/Mso[a-zA-Z]+/)) {
44179                     return;
44180                 }
44181                 cna.push(cls);
44182             });
44183             node.className = cna.length ? cna.join(' ') : '';
44184             if (!cna.length) {
44185                 node.removeAttribute("class");
44186             }
44187         }
44188         
44189         if (node.hasAttribute("lang")) {
44190             node.removeAttribute("lang");
44191         }
44192         
44193         if (node.hasAttribute("style")) {
44194             
44195             var styles = node.getAttribute("style").split(";");
44196             var nstyle = [];
44197             Roo.each(styles, function(s) {
44198                 if (!s.match(/:/)) {
44199                     return;
44200                 }
44201                 var kv = s.split(":");
44202                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44203                     return;
44204                 }
44205                 // what ever is left... we allow.
44206                 nstyle.push(s);
44207             });
44208             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44209             if (!nstyle.length) {
44210                 node.removeAttribute('style');
44211             }
44212         }
44213         this.iterateChildren(node, this.cleanWord);
44214         
44215         
44216         
44217     },
44218     /**
44219      * iterateChildren of a Node, calling fn each time, using this as the scole..
44220      * @param {DomNode} node node to iterate children of.
44221      * @param {Function} fn method of this class to call on each item.
44222      */
44223     iterateChildren : function(node, fn)
44224     {
44225         if (!node.childNodes.length) {
44226                 return;
44227         }
44228         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44229            fn.call(this, node.childNodes[i])
44230         }
44231     },
44232     
44233     
44234     /**
44235      * cleanTableWidths.
44236      *
44237      * Quite often pasting from word etc.. results in tables with column and widths.
44238      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44239      *
44240      */
44241     cleanTableWidths : function(node)
44242     {
44243          
44244          
44245         if (!node) {
44246             this.cleanTableWidths(this.doc.body);
44247             return;
44248         }
44249         
44250         // ignore list...
44251         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44252             return; 
44253         }
44254         Roo.log(node.tagName);
44255         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44256             this.iterateChildren(node, this.cleanTableWidths);
44257             return;
44258         }
44259         if (node.hasAttribute('width')) {
44260             node.removeAttribute('width');
44261         }
44262         
44263          
44264         if (node.hasAttribute("style")) {
44265             // pretty basic...
44266             
44267             var styles = node.getAttribute("style").split(";");
44268             var nstyle = [];
44269             Roo.each(styles, function(s) {
44270                 if (!s.match(/:/)) {
44271                     return;
44272                 }
44273                 var kv = s.split(":");
44274                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44275                     return;
44276                 }
44277                 // what ever is left... we allow.
44278                 nstyle.push(s);
44279             });
44280             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44281             if (!nstyle.length) {
44282                 node.removeAttribute('style');
44283             }
44284         }
44285         
44286         this.iterateChildren(node, this.cleanTableWidths);
44287         
44288         
44289     },
44290     
44291     
44292     
44293     
44294     domToHTML : function(currentElement, depth, nopadtext) {
44295         
44296         depth = depth || 0;
44297         nopadtext = nopadtext || false;
44298     
44299         if (!currentElement) {
44300             return this.domToHTML(this.doc.body);
44301         }
44302         
44303         //Roo.log(currentElement);
44304         var j;
44305         var allText = false;
44306         var nodeName = currentElement.nodeName;
44307         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44308         
44309         if  (nodeName == '#text') {
44310             
44311             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44312         }
44313         
44314         
44315         var ret = '';
44316         if (nodeName != 'BODY') {
44317              
44318             var i = 0;
44319             // Prints the node tagName, such as <A>, <IMG>, etc
44320             if (tagName) {
44321                 var attr = [];
44322                 for(i = 0; i < currentElement.attributes.length;i++) {
44323                     // quoting?
44324                     var aname = currentElement.attributes.item(i).name;
44325                     if (!currentElement.attributes.item(i).value.length) {
44326                         continue;
44327                     }
44328                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44329                 }
44330                 
44331                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44332             } 
44333             else {
44334                 
44335                 // eack
44336             }
44337         } else {
44338             tagName = false;
44339         }
44340         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44341             return ret;
44342         }
44343         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44344             nopadtext = true;
44345         }
44346         
44347         
44348         // Traverse the tree
44349         i = 0;
44350         var currentElementChild = currentElement.childNodes.item(i);
44351         var allText = true;
44352         var innerHTML  = '';
44353         lastnode = '';
44354         while (currentElementChild) {
44355             // Formatting code (indent the tree so it looks nice on the screen)
44356             var nopad = nopadtext;
44357             if (lastnode == 'SPAN') {
44358                 nopad  = true;
44359             }
44360             // text
44361             if  (currentElementChild.nodeName == '#text') {
44362                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44363                 toadd = nopadtext ? toadd : toadd.trim();
44364                 if (!nopad && toadd.length > 80) {
44365                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44366                 }
44367                 innerHTML  += toadd;
44368                 
44369                 i++;
44370                 currentElementChild = currentElement.childNodes.item(i);
44371                 lastNode = '';
44372                 continue;
44373             }
44374             allText = false;
44375             
44376             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44377                 
44378             // Recursively traverse the tree structure of the child node
44379             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44380             lastnode = currentElementChild.nodeName;
44381             i++;
44382             currentElementChild=currentElement.childNodes.item(i);
44383         }
44384         
44385         ret += innerHTML;
44386         
44387         if (!allText) {
44388                 // The remaining code is mostly for formatting the tree
44389             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44390         }
44391         
44392         
44393         if (tagName) {
44394             ret+= "</"+tagName+">";
44395         }
44396         return ret;
44397         
44398     },
44399         
44400     applyBlacklists : function()
44401     {
44402         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44403         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44404         
44405         this.white = [];
44406         this.black = [];
44407         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44408             if (b.indexOf(tag) > -1) {
44409                 return;
44410             }
44411             this.white.push(tag);
44412             
44413         }, this);
44414         
44415         Roo.each(w, function(tag) {
44416             if (b.indexOf(tag) > -1) {
44417                 return;
44418             }
44419             if (this.white.indexOf(tag) > -1) {
44420                 return;
44421             }
44422             this.white.push(tag);
44423             
44424         }, this);
44425         
44426         
44427         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44428             if (w.indexOf(tag) > -1) {
44429                 return;
44430             }
44431             this.black.push(tag);
44432             
44433         }, this);
44434         
44435         Roo.each(b, function(tag) {
44436             if (w.indexOf(tag) > -1) {
44437                 return;
44438             }
44439             if (this.black.indexOf(tag) > -1) {
44440                 return;
44441             }
44442             this.black.push(tag);
44443             
44444         }, this);
44445         
44446         
44447         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44448         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44449         
44450         this.cwhite = [];
44451         this.cblack = [];
44452         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44453             if (b.indexOf(tag) > -1) {
44454                 return;
44455             }
44456             this.cwhite.push(tag);
44457             
44458         }, this);
44459         
44460         Roo.each(w, function(tag) {
44461             if (b.indexOf(tag) > -1) {
44462                 return;
44463             }
44464             if (this.cwhite.indexOf(tag) > -1) {
44465                 return;
44466             }
44467             this.cwhite.push(tag);
44468             
44469         }, this);
44470         
44471         
44472         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44473             if (w.indexOf(tag) > -1) {
44474                 return;
44475             }
44476             this.cblack.push(tag);
44477             
44478         }, this);
44479         
44480         Roo.each(b, function(tag) {
44481             if (w.indexOf(tag) > -1) {
44482                 return;
44483             }
44484             if (this.cblack.indexOf(tag) > -1) {
44485                 return;
44486             }
44487             this.cblack.push(tag);
44488             
44489         }, this);
44490     },
44491     
44492     setStylesheets : function(stylesheets)
44493     {
44494         if(typeof(stylesheets) == 'string'){
44495             Roo.get(this.iframe.contentDocument.head).createChild({
44496                 tag : 'link',
44497                 rel : 'stylesheet',
44498                 type : 'text/css',
44499                 href : stylesheets
44500             });
44501             
44502             return;
44503         }
44504         var _this = this;
44505      
44506         Roo.each(stylesheets, function(s) {
44507             if(!s.length){
44508                 return;
44509             }
44510             
44511             Roo.get(_this.iframe.contentDocument.head).createChild({
44512                 tag : 'link',
44513                 rel : 'stylesheet',
44514                 type : 'text/css',
44515                 href : s
44516             });
44517         });
44518
44519         
44520     },
44521     
44522     removeStylesheets : function()
44523     {
44524         var _this = this;
44525         
44526         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44527             s.remove();
44528         });
44529     },
44530     
44531     setStyle : function(style)
44532     {
44533         Roo.get(this.iframe.contentDocument.head).createChild({
44534             tag : 'style',
44535             type : 'text/css',
44536             html : style
44537         });
44538
44539         return;
44540     }
44541     
44542     // hide stuff that is not compatible
44543     /**
44544      * @event blur
44545      * @hide
44546      */
44547     /**
44548      * @event change
44549      * @hide
44550      */
44551     /**
44552      * @event focus
44553      * @hide
44554      */
44555     /**
44556      * @event specialkey
44557      * @hide
44558      */
44559     /**
44560      * @cfg {String} fieldClass @hide
44561      */
44562     /**
44563      * @cfg {String} focusClass @hide
44564      */
44565     /**
44566      * @cfg {String} autoCreate @hide
44567      */
44568     /**
44569      * @cfg {String} inputType @hide
44570      */
44571     /**
44572      * @cfg {String} invalidClass @hide
44573      */
44574     /**
44575      * @cfg {String} invalidText @hide
44576      */
44577     /**
44578      * @cfg {String} msgFx @hide
44579      */
44580     /**
44581      * @cfg {String} validateOnBlur @hide
44582      */
44583 });
44584
44585 Roo.HtmlEditorCore.white = [
44586         'area', 'br', 'img', 'input', 'hr', 'wbr',
44587         
44588        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44589        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44590        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44591        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44592        'table',   'ul',         'xmp', 
44593        
44594        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44595       'thead',   'tr', 
44596      
44597       'dir', 'menu', 'ol', 'ul', 'dl',
44598        
44599       'embed',  'object'
44600 ];
44601
44602
44603 Roo.HtmlEditorCore.black = [
44604     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44605         'applet', // 
44606         'base',   'basefont', 'bgsound', 'blink',  'body', 
44607         'frame',  'frameset', 'head',    'html',   'ilayer', 
44608         'iframe', 'layer',  'link',     'meta',    'object',   
44609         'script', 'style' ,'title',  'xml' // clean later..
44610 ];
44611 Roo.HtmlEditorCore.clean = [
44612     'script', 'style', 'title', 'xml'
44613 ];
44614 Roo.HtmlEditorCore.remove = [
44615     'font'
44616 ];
44617 // attributes..
44618
44619 Roo.HtmlEditorCore.ablack = [
44620     'on'
44621 ];
44622     
44623 Roo.HtmlEditorCore.aclean = [ 
44624     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44625 ];
44626
44627 // protocols..
44628 Roo.HtmlEditorCore.pwhite= [
44629         'http',  'https',  'mailto'
44630 ];
44631
44632 // white listed style attributes.
44633 Roo.HtmlEditorCore.cwhite= [
44634       //  'text-align', /// default is to allow most things..
44635       
44636          
44637 //        'font-size'//??
44638 ];
44639
44640 // black listed style attributes.
44641 Roo.HtmlEditorCore.cblack= [
44642       //  'font-size' -- this can be set by the project 
44643 ];
44644
44645
44646 Roo.HtmlEditorCore.swapCodes   =[ 
44647     [    8211, "--" ], 
44648     [    8212, "--" ], 
44649     [    8216,  "'" ],  
44650     [    8217, "'" ],  
44651     [    8220, '"' ],  
44652     [    8221, '"' ],  
44653     [    8226, "*" ],  
44654     [    8230, "..." ]
44655 ]; 
44656
44657     //<script type="text/javascript">
44658
44659 /*
44660  * Ext JS Library 1.1.1
44661  * Copyright(c) 2006-2007, Ext JS, LLC.
44662  * Licence LGPL
44663  * 
44664  */
44665  
44666  
44667 Roo.form.HtmlEditor = function(config){
44668     
44669     
44670     
44671     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44672     
44673     if (!this.toolbars) {
44674         this.toolbars = [];
44675     }
44676     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44677     
44678     
44679 };
44680
44681 /**
44682  * @class Roo.form.HtmlEditor
44683  * @extends Roo.form.Field
44684  * Provides a lightweight HTML Editor component.
44685  *
44686  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44687  * 
44688  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44689  * supported by this editor.</b><br/><br/>
44690  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44691  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44692  */
44693 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44694     /**
44695      * @cfg {Boolean} clearUp
44696      */
44697     clearUp : true,
44698       /**
44699      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44700      */
44701     toolbars : false,
44702    
44703      /**
44704      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44705      *                        Roo.resizable.
44706      */
44707     resizable : false,
44708      /**
44709      * @cfg {Number} height (in pixels)
44710      */   
44711     height: 300,
44712    /**
44713      * @cfg {Number} width (in pixels)
44714      */   
44715     width: 500,
44716     
44717     /**
44718      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44719      * 
44720      */
44721     stylesheets: false,
44722     
44723     
44724      /**
44725      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44726      * 
44727      */
44728     cblack: false,
44729     /**
44730      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44731      * 
44732      */
44733     cwhite: false,
44734     
44735      /**
44736      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44737      * 
44738      */
44739     black: false,
44740     /**
44741      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44742      * 
44743      */
44744     white: false,
44745     
44746     // id of frame..
44747     frameId: false,
44748     
44749     // private properties
44750     validationEvent : false,
44751     deferHeight: true,
44752     initialized : false,
44753     activated : false,
44754     
44755     onFocus : Roo.emptyFn,
44756     iframePad:3,
44757     hideMode:'offsets',
44758     
44759     actionMode : 'container', // defaults to hiding it...
44760     
44761     defaultAutoCreate : { // modified by initCompnoent..
44762         tag: "textarea",
44763         style:"width:500px;height:300px;",
44764         autocomplete: "new-password"
44765     },
44766
44767     // private
44768     initComponent : function(){
44769         this.addEvents({
44770             /**
44771              * @event initialize
44772              * Fires when the editor is fully initialized (including the iframe)
44773              * @param {HtmlEditor} this
44774              */
44775             initialize: true,
44776             /**
44777              * @event activate
44778              * Fires when the editor is first receives the focus. Any insertion must wait
44779              * until after this event.
44780              * @param {HtmlEditor} this
44781              */
44782             activate: true,
44783              /**
44784              * @event beforesync
44785              * Fires before the textarea is updated with content from the editor iframe. Return false
44786              * to cancel the sync.
44787              * @param {HtmlEditor} this
44788              * @param {String} html
44789              */
44790             beforesync: true,
44791              /**
44792              * @event beforepush
44793              * Fires before the iframe editor is updated with content from the textarea. Return false
44794              * to cancel the push.
44795              * @param {HtmlEditor} this
44796              * @param {String} html
44797              */
44798             beforepush: true,
44799              /**
44800              * @event sync
44801              * Fires when the textarea is updated with content from the editor iframe.
44802              * @param {HtmlEditor} this
44803              * @param {String} html
44804              */
44805             sync: true,
44806              /**
44807              * @event push
44808              * Fires when the iframe editor is updated with content from the textarea.
44809              * @param {HtmlEditor} this
44810              * @param {String} html
44811              */
44812             push: true,
44813              /**
44814              * @event editmodechange
44815              * Fires when the editor switches edit modes
44816              * @param {HtmlEditor} this
44817              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44818              */
44819             editmodechange: true,
44820             /**
44821              * @event editorevent
44822              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44823              * @param {HtmlEditor} this
44824              */
44825             editorevent: true,
44826             /**
44827              * @event firstfocus
44828              * Fires when on first focus - needed by toolbars..
44829              * @param {HtmlEditor} this
44830              */
44831             firstfocus: true,
44832             /**
44833              * @event autosave
44834              * Auto save the htmlEditor value as a file into Events
44835              * @param {HtmlEditor} this
44836              */
44837             autosave: true,
44838             /**
44839              * @event savedpreview
44840              * preview the saved version of htmlEditor
44841              * @param {HtmlEditor} this
44842              */
44843             savedpreview: true,
44844             
44845             /**
44846             * @event stylesheetsclick
44847             * Fires when press the Sytlesheets button
44848             * @param {Roo.HtmlEditorCore} this
44849             */
44850             stylesheetsclick: true
44851         });
44852         this.defaultAutoCreate =  {
44853             tag: "textarea",
44854             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44855             autocomplete: "new-password"
44856         };
44857     },
44858
44859     /**
44860      * Protected method that will not generally be called directly. It
44861      * is called when the editor creates its toolbar. Override this method if you need to
44862      * add custom toolbar buttons.
44863      * @param {HtmlEditor} editor
44864      */
44865     createToolbar : function(editor){
44866         Roo.log("create toolbars");
44867         if (!editor.toolbars || !editor.toolbars.length) {
44868             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44869         }
44870         
44871         for (var i =0 ; i < editor.toolbars.length;i++) {
44872             editor.toolbars[i] = Roo.factory(
44873                     typeof(editor.toolbars[i]) == 'string' ?
44874                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44875                 Roo.form.HtmlEditor);
44876             editor.toolbars[i].init(editor);
44877         }
44878          
44879         
44880     },
44881
44882      
44883     // private
44884     onRender : function(ct, position)
44885     {
44886         var _t = this;
44887         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44888         
44889         this.wrap = this.el.wrap({
44890             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44891         });
44892         
44893         this.editorcore.onRender(ct, position);
44894          
44895         if (this.resizable) {
44896             this.resizeEl = new Roo.Resizable(this.wrap, {
44897                 pinned : true,
44898                 wrap: true,
44899                 dynamic : true,
44900                 minHeight : this.height,
44901                 height: this.height,
44902                 handles : this.resizable,
44903                 width: this.width,
44904                 listeners : {
44905                     resize : function(r, w, h) {
44906                         _t.onResize(w,h); // -something
44907                     }
44908                 }
44909             });
44910             
44911         }
44912         this.createToolbar(this);
44913        
44914         
44915         if(!this.width){
44916             this.setSize(this.wrap.getSize());
44917         }
44918         if (this.resizeEl) {
44919             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44920             // should trigger onReize..
44921         }
44922         
44923         this.keyNav = new Roo.KeyNav(this.el, {
44924             
44925             "tab" : function(e){
44926                 e.preventDefault();
44927                 
44928                 var value = this.getValue();
44929                 
44930                 var start = this.el.dom.selectionStart;
44931                 var end = this.el.dom.selectionEnd;
44932                 
44933                 if(!e.shiftKey){
44934                     
44935                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44936                     this.el.dom.setSelectionRange(end + 1, end + 1);
44937                     return;
44938                 }
44939                 
44940                 var f = value.substring(0, start).split("\t");
44941                 
44942                 if(f.pop().length != 0){
44943                     return;
44944                 }
44945                 
44946                 this.setValue(f.join("\t") + value.substring(end));
44947                 this.el.dom.setSelectionRange(start - 1, start - 1);
44948                 
44949             },
44950             
44951             "home" : function(e){
44952                 e.preventDefault();
44953                 
44954                 var curr = this.el.dom.selectionStart;
44955                 var lines = this.getValue().split("\n");
44956                 
44957                 if(!lines.length){
44958                     return;
44959                 }
44960                 
44961                 if(e.ctrlKey){
44962                     this.el.dom.setSelectionRange(0, 0);
44963                     return;
44964                 }
44965                 
44966                 var pos = 0;
44967                 
44968                 for (var i = 0; i < lines.length;i++) {
44969                     pos += lines[i].length;
44970                     
44971                     if(i != 0){
44972                         pos += 1;
44973                     }
44974                     
44975                     if(pos < curr){
44976                         continue;
44977                     }
44978                     
44979                     pos -= lines[i].length;
44980                     
44981                     break;
44982                 }
44983                 
44984                 if(!e.shiftKey){
44985                     this.el.dom.setSelectionRange(pos, pos);
44986                     return;
44987                 }
44988                 
44989                 this.el.dom.selectionStart = pos;
44990                 this.el.dom.selectionEnd = curr;
44991             },
44992             
44993             "end" : function(e){
44994                 e.preventDefault();
44995                 
44996                 var curr = this.el.dom.selectionStart;
44997                 var lines = this.getValue().split("\n");
44998                 
44999                 if(!lines.length){
45000                     return;
45001                 }
45002                 
45003                 if(e.ctrlKey){
45004                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45005                     return;
45006                 }
45007                 
45008                 var pos = 0;
45009                 
45010                 for (var i = 0; i < lines.length;i++) {
45011                     
45012                     pos += lines[i].length;
45013                     
45014                     if(i != 0){
45015                         pos += 1;
45016                     }
45017                     
45018                     if(pos < curr){
45019                         continue;
45020                     }
45021                     
45022                     break;
45023                 }
45024                 
45025                 if(!e.shiftKey){
45026                     this.el.dom.setSelectionRange(pos, pos);
45027                     return;
45028                 }
45029                 
45030                 this.el.dom.selectionStart = curr;
45031                 this.el.dom.selectionEnd = pos;
45032             },
45033
45034             scope : this,
45035
45036             doRelay : function(foo, bar, hname){
45037                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45038             },
45039
45040             forceKeyDown: true
45041         });
45042         
45043 //        if(this.autosave && this.w){
45044 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45045 //        }
45046     },
45047
45048     // private
45049     onResize : function(w, h)
45050     {
45051         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45052         var ew = false;
45053         var eh = false;
45054         
45055         if(this.el ){
45056             if(typeof w == 'number'){
45057                 var aw = w - this.wrap.getFrameWidth('lr');
45058                 this.el.setWidth(this.adjustWidth('textarea', aw));
45059                 ew = aw;
45060             }
45061             if(typeof h == 'number'){
45062                 var tbh = 0;
45063                 for (var i =0; i < this.toolbars.length;i++) {
45064                     // fixme - ask toolbars for heights?
45065                     tbh += this.toolbars[i].tb.el.getHeight();
45066                     if (this.toolbars[i].footer) {
45067                         tbh += this.toolbars[i].footer.el.getHeight();
45068                     }
45069                 }
45070                 
45071                 
45072                 
45073                 
45074                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45075                 ah -= 5; // knock a few pixes off for look..
45076 //                Roo.log(ah);
45077                 this.el.setHeight(this.adjustWidth('textarea', ah));
45078                 var eh = ah;
45079             }
45080         }
45081         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45082         this.editorcore.onResize(ew,eh);
45083         
45084     },
45085
45086     /**
45087      * Toggles the editor between standard and source edit mode.
45088      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45089      */
45090     toggleSourceEdit : function(sourceEditMode)
45091     {
45092         this.editorcore.toggleSourceEdit(sourceEditMode);
45093         
45094         if(this.editorcore.sourceEditMode){
45095             Roo.log('editor - showing textarea');
45096             
45097 //            Roo.log('in');
45098 //            Roo.log(this.syncValue());
45099             this.editorcore.syncValue();
45100             this.el.removeClass('x-hidden');
45101             this.el.dom.removeAttribute('tabIndex');
45102             this.el.focus();
45103             
45104             for (var i = 0; i < this.toolbars.length; i++) {
45105                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45106                     this.toolbars[i].tb.hide();
45107                     this.toolbars[i].footer.hide();
45108                 }
45109             }
45110             
45111         }else{
45112             Roo.log('editor - hiding textarea');
45113 //            Roo.log('out')
45114 //            Roo.log(this.pushValue()); 
45115             this.editorcore.pushValue();
45116             
45117             this.el.addClass('x-hidden');
45118             this.el.dom.setAttribute('tabIndex', -1);
45119             
45120             for (var i = 0; i < this.toolbars.length; i++) {
45121                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45122                     this.toolbars[i].tb.show();
45123                     this.toolbars[i].footer.show();
45124                 }
45125             }
45126             
45127             //this.deferFocus();
45128         }
45129         
45130         this.setSize(this.wrap.getSize());
45131         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45132         
45133         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45134     },
45135  
45136     // private (for BoxComponent)
45137     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45138
45139     // private (for BoxComponent)
45140     getResizeEl : function(){
45141         return this.wrap;
45142     },
45143
45144     // private (for BoxComponent)
45145     getPositionEl : function(){
45146         return this.wrap;
45147     },
45148
45149     // private
45150     initEvents : function(){
45151         this.originalValue = this.getValue();
45152     },
45153
45154     /**
45155      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45156      * @method
45157      */
45158     markInvalid : Roo.emptyFn,
45159     /**
45160      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45161      * @method
45162      */
45163     clearInvalid : Roo.emptyFn,
45164
45165     setValue : function(v){
45166         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45167         this.editorcore.pushValue();
45168     },
45169
45170      
45171     // private
45172     deferFocus : function(){
45173         this.focus.defer(10, this);
45174     },
45175
45176     // doc'ed in Field
45177     focus : function(){
45178         this.editorcore.focus();
45179         
45180     },
45181       
45182
45183     // private
45184     onDestroy : function(){
45185         
45186         
45187         
45188         if(this.rendered){
45189             
45190             for (var i =0; i < this.toolbars.length;i++) {
45191                 // fixme - ask toolbars for heights?
45192                 this.toolbars[i].onDestroy();
45193             }
45194             
45195             this.wrap.dom.innerHTML = '';
45196             this.wrap.remove();
45197         }
45198     },
45199
45200     // private
45201     onFirstFocus : function(){
45202         //Roo.log("onFirstFocus");
45203         this.editorcore.onFirstFocus();
45204          for (var i =0; i < this.toolbars.length;i++) {
45205             this.toolbars[i].onFirstFocus();
45206         }
45207         
45208     },
45209     
45210     // private
45211     syncValue : function()
45212     {
45213         this.editorcore.syncValue();
45214     },
45215     
45216     pushValue : function()
45217     {
45218         this.editorcore.pushValue();
45219     },
45220     
45221     setStylesheets : function(stylesheets)
45222     {
45223         this.editorcore.setStylesheets(stylesheets);
45224     },
45225     
45226     removeStylesheets : function()
45227     {
45228         this.editorcore.removeStylesheets();
45229     }
45230      
45231     
45232     // hide stuff that is not compatible
45233     /**
45234      * @event blur
45235      * @hide
45236      */
45237     /**
45238      * @event change
45239      * @hide
45240      */
45241     /**
45242      * @event focus
45243      * @hide
45244      */
45245     /**
45246      * @event specialkey
45247      * @hide
45248      */
45249     /**
45250      * @cfg {String} fieldClass @hide
45251      */
45252     /**
45253      * @cfg {String} focusClass @hide
45254      */
45255     /**
45256      * @cfg {String} autoCreate @hide
45257      */
45258     /**
45259      * @cfg {String} inputType @hide
45260      */
45261     /**
45262      * @cfg {String} invalidClass @hide
45263      */
45264     /**
45265      * @cfg {String} invalidText @hide
45266      */
45267     /**
45268      * @cfg {String} msgFx @hide
45269      */
45270     /**
45271      * @cfg {String} validateOnBlur @hide
45272      */
45273 });
45274  
45275     // <script type="text/javascript">
45276 /*
45277  * Based on
45278  * Ext JS Library 1.1.1
45279  * Copyright(c) 2006-2007, Ext JS, LLC.
45280  *  
45281  
45282  */
45283
45284 /**
45285  * @class Roo.form.HtmlEditorToolbar1
45286  * Basic Toolbar
45287  * 
45288  * Usage:
45289  *
45290  new Roo.form.HtmlEditor({
45291     ....
45292     toolbars : [
45293         new Roo.form.HtmlEditorToolbar1({
45294             disable : { fonts: 1 , format: 1, ..., ... , ...],
45295             btns : [ .... ]
45296         })
45297     }
45298      
45299  * 
45300  * @cfg {Object} disable List of elements to disable..
45301  * @cfg {Array} btns List of additional buttons.
45302  * 
45303  * 
45304  * NEEDS Extra CSS? 
45305  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45306  */
45307  
45308 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45309 {
45310     
45311     Roo.apply(this, config);
45312     
45313     // default disabled, based on 'good practice'..
45314     this.disable = this.disable || {};
45315     Roo.applyIf(this.disable, {
45316         fontSize : true,
45317         colors : true,
45318         specialElements : true
45319     });
45320     
45321     
45322     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45323     // dont call parent... till later.
45324 }
45325
45326 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45327     
45328     tb: false,
45329     
45330     rendered: false,
45331     
45332     editor : false,
45333     editorcore : false,
45334     /**
45335      * @cfg {Object} disable  List of toolbar elements to disable
45336          
45337      */
45338     disable : false,
45339     
45340     
45341      /**
45342      * @cfg {String} createLinkText The default text for the create link prompt
45343      */
45344     createLinkText : 'Please enter the URL for the link:',
45345     /**
45346      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45347      */
45348     defaultLinkValue : 'http:/'+'/',
45349    
45350     
45351       /**
45352      * @cfg {Array} fontFamilies An array of available font families
45353      */
45354     fontFamilies : [
45355         'Arial',
45356         'Courier New',
45357         'Tahoma',
45358         'Times New Roman',
45359         'Verdana'
45360     ],
45361     
45362     specialChars : [
45363            "&#169;",
45364           "&#174;",     
45365           "&#8482;",    
45366           "&#163;" ,    
45367          // "&#8212;",    
45368           "&#8230;",    
45369           "&#247;" ,    
45370         //  "&#225;" ,     ?? a acute?
45371            "&#8364;"    , //Euro
45372        //   "&#8220;"    ,
45373         //  "&#8221;"    ,
45374         //  "&#8226;"    ,
45375           "&#176;"  //   , // degrees
45376
45377          // "&#233;"     , // e ecute
45378          // "&#250;"     , // u ecute?
45379     ],
45380     
45381     specialElements : [
45382         {
45383             text: "Insert Table",
45384             xtype: 'MenuItem',
45385             xns : Roo.Menu,
45386             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45387                 
45388         },
45389         {    
45390             text: "Insert Image",
45391             xtype: 'MenuItem',
45392             xns : Roo.Menu,
45393             ihtml : '<img src="about:blank"/>'
45394             
45395         }
45396         
45397          
45398     ],
45399     
45400     
45401     inputElements : [ 
45402             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45403             "input:submit", "input:button", "select", "textarea", "label" ],
45404     formats : [
45405         ["p"] ,  
45406         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45407         ["pre"],[ "code"], 
45408         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45409         ['div'],['span'],
45410         ['sup'],['sub']
45411     ],
45412     
45413     cleanStyles : [
45414         "font-size"
45415     ],
45416      /**
45417      * @cfg {String} defaultFont default font to use.
45418      */
45419     defaultFont: 'tahoma',
45420    
45421     fontSelect : false,
45422     
45423     
45424     formatCombo : false,
45425     
45426     init : function(editor)
45427     {
45428         this.editor = editor;
45429         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45430         var editorcore = this.editorcore;
45431         
45432         var _t = this;
45433         
45434         var fid = editorcore.frameId;
45435         var etb = this;
45436         function btn(id, toggle, handler){
45437             var xid = fid + '-'+ id ;
45438             return {
45439                 id : xid,
45440                 cmd : id,
45441                 cls : 'x-btn-icon x-edit-'+id,
45442                 enableToggle:toggle !== false,
45443                 scope: _t, // was editor...
45444                 handler:handler||_t.relayBtnCmd,
45445                 clickEvent:'mousedown',
45446                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45447                 tabIndex:-1
45448             };
45449         }
45450         
45451         
45452         
45453         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45454         this.tb = tb;
45455          // stop form submits
45456         tb.el.on('click', function(e){
45457             e.preventDefault(); // what does this do?
45458         });
45459
45460         if(!this.disable.font) { // && !Roo.isSafari){
45461             /* why no safari for fonts 
45462             editor.fontSelect = tb.el.createChild({
45463                 tag:'select',
45464                 tabIndex: -1,
45465                 cls:'x-font-select',
45466                 html: this.createFontOptions()
45467             });
45468             
45469             editor.fontSelect.on('change', function(){
45470                 var font = editor.fontSelect.dom.value;
45471                 editor.relayCmd('fontname', font);
45472                 editor.deferFocus();
45473             }, editor);
45474             
45475             tb.add(
45476                 editor.fontSelect.dom,
45477                 '-'
45478             );
45479             */
45480             
45481         };
45482         if(!this.disable.formats){
45483             this.formatCombo = new Roo.form.ComboBox({
45484                 store: new Roo.data.SimpleStore({
45485                     id : 'tag',
45486                     fields: ['tag'],
45487                     data : this.formats // from states.js
45488                 }),
45489                 blockFocus : true,
45490                 name : '',
45491                 //autoCreate : {tag: "div",  size: "20"},
45492                 displayField:'tag',
45493                 typeAhead: false,
45494                 mode: 'local',
45495                 editable : false,
45496                 triggerAction: 'all',
45497                 emptyText:'Add tag',
45498                 selectOnFocus:true,
45499                 width:135,
45500                 listeners : {
45501                     'select': function(c, r, i) {
45502                         editorcore.insertTag(r.get('tag'));
45503                         editor.focus();
45504                     }
45505                 }
45506
45507             });
45508             tb.addField(this.formatCombo);
45509             
45510         }
45511         
45512         if(!this.disable.format){
45513             tb.add(
45514                 btn('bold'),
45515                 btn('italic'),
45516                 btn('underline'),
45517                 btn('strikethrough')
45518             );
45519         };
45520         if(!this.disable.fontSize){
45521             tb.add(
45522                 '-',
45523                 
45524                 
45525                 btn('increasefontsize', false, editorcore.adjustFont),
45526                 btn('decreasefontsize', false, editorcore.adjustFont)
45527             );
45528         };
45529         
45530         
45531         if(!this.disable.colors){
45532             tb.add(
45533                 '-', {
45534                     id:editorcore.frameId +'-forecolor',
45535                     cls:'x-btn-icon x-edit-forecolor',
45536                     clickEvent:'mousedown',
45537                     tooltip: this.buttonTips['forecolor'] || undefined,
45538                     tabIndex:-1,
45539                     menu : new Roo.menu.ColorMenu({
45540                         allowReselect: true,
45541                         focus: Roo.emptyFn,
45542                         value:'000000',
45543                         plain:true,
45544                         selectHandler: function(cp, color){
45545                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45546                             editor.deferFocus();
45547                         },
45548                         scope: editorcore,
45549                         clickEvent:'mousedown'
45550                     })
45551                 }, {
45552                     id:editorcore.frameId +'backcolor',
45553                     cls:'x-btn-icon x-edit-backcolor',
45554                     clickEvent:'mousedown',
45555                     tooltip: this.buttonTips['backcolor'] || undefined,
45556                     tabIndex:-1,
45557                     menu : new Roo.menu.ColorMenu({
45558                         focus: Roo.emptyFn,
45559                         value:'FFFFFF',
45560                         plain:true,
45561                         allowReselect: true,
45562                         selectHandler: function(cp, color){
45563                             if(Roo.isGecko){
45564                                 editorcore.execCmd('useCSS', false);
45565                                 editorcore.execCmd('hilitecolor', color);
45566                                 editorcore.execCmd('useCSS', true);
45567                                 editor.deferFocus();
45568                             }else{
45569                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45570                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45571                                 editor.deferFocus();
45572                             }
45573                         },
45574                         scope:editorcore,
45575                         clickEvent:'mousedown'
45576                     })
45577                 }
45578             );
45579         };
45580         // now add all the items...
45581         
45582
45583         if(!this.disable.alignments){
45584             tb.add(
45585                 '-',
45586                 btn('justifyleft'),
45587                 btn('justifycenter'),
45588                 btn('justifyright')
45589             );
45590         };
45591
45592         //if(!Roo.isSafari){
45593             if(!this.disable.links){
45594                 tb.add(
45595                     '-',
45596                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45597                 );
45598             };
45599
45600             if(!this.disable.lists){
45601                 tb.add(
45602                     '-',
45603                     btn('insertorderedlist'),
45604                     btn('insertunorderedlist')
45605                 );
45606             }
45607             if(!this.disable.sourceEdit){
45608                 tb.add(
45609                     '-',
45610                     btn('sourceedit', true, function(btn){
45611                         this.toggleSourceEdit(btn.pressed);
45612                     })
45613                 );
45614             }
45615         //}
45616         
45617         var smenu = { };
45618         // special menu.. - needs to be tidied up..
45619         if (!this.disable.special) {
45620             smenu = {
45621                 text: "&#169;",
45622                 cls: 'x-edit-none',
45623                 
45624                 menu : {
45625                     items : []
45626                 }
45627             };
45628             for (var i =0; i < this.specialChars.length; i++) {
45629                 smenu.menu.items.push({
45630                     
45631                     html: this.specialChars[i],
45632                     handler: function(a,b) {
45633                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45634                         //editor.insertAtCursor(a.html);
45635                         
45636                     },
45637                     tabIndex:-1
45638                 });
45639             }
45640             
45641             
45642             tb.add(smenu);
45643             
45644             
45645         }
45646         
45647         var cmenu = { };
45648         if (!this.disable.cleanStyles) {
45649             cmenu = {
45650                 cls: 'x-btn-icon x-btn-clear',
45651                 
45652                 menu : {
45653                     items : []
45654                 }
45655             };
45656             for (var i =0; i < this.cleanStyles.length; i++) {
45657                 cmenu.menu.items.push({
45658                     actiontype : this.cleanStyles[i],
45659                     html: 'Remove ' + this.cleanStyles[i],
45660                     handler: function(a,b) {
45661 //                        Roo.log(a);
45662 //                        Roo.log(b);
45663                         var c = Roo.get(editorcore.doc.body);
45664                         c.select('[style]').each(function(s) {
45665                             s.dom.style.removeProperty(a.actiontype);
45666                         });
45667                         editorcore.syncValue();
45668                     },
45669                     tabIndex:-1
45670                 });
45671             }
45672              cmenu.menu.items.push({
45673                 actiontype : 'tablewidths',
45674                 html: 'Remove Table Widths',
45675                 handler: function(a,b) {
45676                     editorcore.cleanTableWidths();
45677                     editorcore.syncValue();
45678                 },
45679                 tabIndex:-1
45680             });
45681             cmenu.menu.items.push({
45682                 actiontype : 'word',
45683                 html: 'Remove MS Word Formating',
45684                 handler: function(a,b) {
45685                     editorcore.cleanWord();
45686                     editorcore.syncValue();
45687                 },
45688                 tabIndex:-1
45689             });
45690             
45691             cmenu.menu.items.push({
45692                 actiontype : 'all',
45693                 html: 'Remove All Styles',
45694                 handler: function(a,b) {
45695                     
45696                     var c = Roo.get(editorcore.doc.body);
45697                     c.select('[style]').each(function(s) {
45698                         s.dom.removeAttribute('style');
45699                     });
45700                     editorcore.syncValue();
45701                 },
45702                 tabIndex:-1
45703             });
45704             
45705             cmenu.menu.items.push({
45706                 actiontype : 'all',
45707                 html: 'Remove All CSS Classes',
45708                 handler: function(a,b) {
45709                     
45710                     var c = Roo.get(editorcore.doc.body);
45711                     c.select('[class]').each(function(s) {
45712                         s.dom.removeAttribute('class');
45713                     });
45714                     editorcore.cleanWord();
45715                     editorcore.syncValue();
45716                 },
45717                 tabIndex:-1
45718             });
45719             
45720              cmenu.menu.items.push({
45721                 actiontype : 'tidy',
45722                 html: 'Tidy HTML Source',
45723                 handler: function(a,b) {
45724                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45725                     editorcore.syncValue();
45726                 },
45727                 tabIndex:-1
45728             });
45729             
45730             
45731             tb.add(cmenu);
45732         }
45733          
45734         if (!this.disable.specialElements) {
45735             var semenu = {
45736                 text: "Other;",
45737                 cls: 'x-edit-none',
45738                 menu : {
45739                     items : []
45740                 }
45741             };
45742             for (var i =0; i < this.specialElements.length; i++) {
45743                 semenu.menu.items.push(
45744                     Roo.apply({ 
45745                         handler: function(a,b) {
45746                             editor.insertAtCursor(this.ihtml);
45747                         }
45748                     }, this.specialElements[i])
45749                 );
45750                     
45751             }
45752             
45753             tb.add(semenu);
45754             
45755             
45756         }
45757          
45758         
45759         if (this.btns) {
45760             for(var i =0; i< this.btns.length;i++) {
45761                 var b = Roo.factory(this.btns[i],Roo.form);
45762                 b.cls =  'x-edit-none';
45763                 
45764                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45765                     b.cls += ' x-init-enable';
45766                 }
45767                 
45768                 b.scope = editorcore;
45769                 tb.add(b);
45770             }
45771         
45772         }
45773         
45774         
45775         
45776         // disable everything...
45777         
45778         this.tb.items.each(function(item){
45779             
45780            if(
45781                 item.id != editorcore.frameId+ '-sourceedit' && 
45782                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45783             ){
45784                 
45785                 item.disable();
45786             }
45787         });
45788         this.rendered = true;
45789         
45790         // the all the btns;
45791         editor.on('editorevent', this.updateToolbar, this);
45792         // other toolbars need to implement this..
45793         //editor.on('editmodechange', this.updateToolbar, this);
45794     },
45795     
45796     
45797     relayBtnCmd : function(btn) {
45798         this.editorcore.relayCmd(btn.cmd);
45799     },
45800     // private used internally
45801     createLink : function(){
45802         Roo.log("create link?");
45803         var url = prompt(this.createLinkText, this.defaultLinkValue);
45804         if(url && url != 'http:/'+'/'){
45805             this.editorcore.relayCmd('createlink', url);
45806         }
45807     },
45808
45809     
45810     /**
45811      * Protected method that will not generally be called directly. It triggers
45812      * a toolbar update by reading the markup state of the current selection in the editor.
45813      */
45814     updateToolbar: function(){
45815
45816         if(!this.editorcore.activated){
45817             this.editor.onFirstFocus();
45818             return;
45819         }
45820
45821         var btns = this.tb.items.map, 
45822             doc = this.editorcore.doc,
45823             frameId = this.editorcore.frameId;
45824
45825         if(!this.disable.font && !Roo.isSafari){
45826             /*
45827             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45828             if(name != this.fontSelect.dom.value){
45829                 this.fontSelect.dom.value = name;
45830             }
45831             */
45832         }
45833         if(!this.disable.format){
45834             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45835             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45836             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45837             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45838         }
45839         if(!this.disable.alignments){
45840             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45841             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45842             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45843         }
45844         if(!Roo.isSafari && !this.disable.lists){
45845             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45846             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45847         }
45848         
45849         var ans = this.editorcore.getAllAncestors();
45850         if (this.formatCombo) {
45851             
45852             
45853             var store = this.formatCombo.store;
45854             this.formatCombo.setValue("");
45855             for (var i =0; i < ans.length;i++) {
45856                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45857                     // select it..
45858                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45859                     break;
45860                 }
45861             }
45862         }
45863         
45864         
45865         
45866         // hides menus... - so this cant be on a menu...
45867         Roo.menu.MenuMgr.hideAll();
45868
45869         //this.editorsyncValue();
45870     },
45871    
45872     
45873     createFontOptions : function(){
45874         var buf = [], fs = this.fontFamilies, ff, lc;
45875         
45876         
45877         
45878         for(var i = 0, len = fs.length; i< len; i++){
45879             ff = fs[i];
45880             lc = ff.toLowerCase();
45881             buf.push(
45882                 '<option value="',lc,'" style="font-family:',ff,';"',
45883                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45884                     ff,
45885                 '</option>'
45886             );
45887         }
45888         return buf.join('');
45889     },
45890     
45891     toggleSourceEdit : function(sourceEditMode){
45892         
45893         Roo.log("toolbar toogle");
45894         if(sourceEditMode === undefined){
45895             sourceEditMode = !this.sourceEditMode;
45896         }
45897         this.sourceEditMode = sourceEditMode === true;
45898         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45899         // just toggle the button?
45900         if(btn.pressed !== this.sourceEditMode){
45901             btn.toggle(this.sourceEditMode);
45902             return;
45903         }
45904         
45905         if(sourceEditMode){
45906             Roo.log("disabling buttons");
45907             this.tb.items.each(function(item){
45908                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45909                     item.disable();
45910                 }
45911             });
45912           
45913         }else{
45914             Roo.log("enabling buttons");
45915             if(this.editorcore.initialized){
45916                 this.tb.items.each(function(item){
45917                     item.enable();
45918                 });
45919             }
45920             
45921         }
45922         Roo.log("calling toggole on editor");
45923         // tell the editor that it's been pressed..
45924         this.editor.toggleSourceEdit(sourceEditMode);
45925        
45926     },
45927      /**
45928      * Object collection of toolbar tooltips for the buttons in the editor. The key
45929      * is the command id associated with that button and the value is a valid QuickTips object.
45930      * For example:
45931 <pre><code>
45932 {
45933     bold : {
45934         title: 'Bold (Ctrl+B)',
45935         text: 'Make the selected text bold.',
45936         cls: 'x-html-editor-tip'
45937     },
45938     italic : {
45939         title: 'Italic (Ctrl+I)',
45940         text: 'Make the selected text italic.',
45941         cls: 'x-html-editor-tip'
45942     },
45943     ...
45944 </code></pre>
45945     * @type Object
45946      */
45947     buttonTips : {
45948         bold : {
45949             title: 'Bold (Ctrl+B)',
45950             text: 'Make the selected text bold.',
45951             cls: 'x-html-editor-tip'
45952         },
45953         italic : {
45954             title: 'Italic (Ctrl+I)',
45955             text: 'Make the selected text italic.',
45956             cls: 'x-html-editor-tip'
45957         },
45958         underline : {
45959             title: 'Underline (Ctrl+U)',
45960             text: 'Underline the selected text.',
45961             cls: 'x-html-editor-tip'
45962         },
45963         strikethrough : {
45964             title: 'Strikethrough',
45965             text: 'Strikethrough the selected text.',
45966             cls: 'x-html-editor-tip'
45967         },
45968         increasefontsize : {
45969             title: 'Grow Text',
45970             text: 'Increase the font size.',
45971             cls: 'x-html-editor-tip'
45972         },
45973         decreasefontsize : {
45974             title: 'Shrink Text',
45975             text: 'Decrease the font size.',
45976             cls: 'x-html-editor-tip'
45977         },
45978         backcolor : {
45979             title: 'Text Highlight Color',
45980             text: 'Change the background color of the selected text.',
45981             cls: 'x-html-editor-tip'
45982         },
45983         forecolor : {
45984             title: 'Font Color',
45985             text: 'Change the color of the selected text.',
45986             cls: 'x-html-editor-tip'
45987         },
45988         justifyleft : {
45989             title: 'Align Text Left',
45990             text: 'Align text to the left.',
45991             cls: 'x-html-editor-tip'
45992         },
45993         justifycenter : {
45994             title: 'Center Text',
45995             text: 'Center text in the editor.',
45996             cls: 'x-html-editor-tip'
45997         },
45998         justifyright : {
45999             title: 'Align Text Right',
46000             text: 'Align text to the right.',
46001             cls: 'x-html-editor-tip'
46002         },
46003         insertunorderedlist : {
46004             title: 'Bullet List',
46005             text: 'Start a bulleted list.',
46006             cls: 'x-html-editor-tip'
46007         },
46008         insertorderedlist : {
46009             title: 'Numbered List',
46010             text: 'Start a numbered list.',
46011             cls: 'x-html-editor-tip'
46012         },
46013         createlink : {
46014             title: 'Hyperlink',
46015             text: 'Make the selected text a hyperlink.',
46016             cls: 'x-html-editor-tip'
46017         },
46018         sourceedit : {
46019             title: 'Source Edit',
46020             text: 'Switch to source editing mode.',
46021             cls: 'x-html-editor-tip'
46022         }
46023     },
46024     // private
46025     onDestroy : function(){
46026         if(this.rendered){
46027             
46028             this.tb.items.each(function(item){
46029                 if(item.menu){
46030                     item.menu.removeAll();
46031                     if(item.menu.el){
46032                         item.menu.el.destroy();
46033                     }
46034                 }
46035                 item.destroy();
46036             });
46037              
46038         }
46039     },
46040     onFirstFocus: function() {
46041         this.tb.items.each(function(item){
46042            item.enable();
46043         });
46044     }
46045 });
46046
46047
46048
46049
46050 // <script type="text/javascript">
46051 /*
46052  * Based on
46053  * Ext JS Library 1.1.1
46054  * Copyright(c) 2006-2007, Ext JS, LLC.
46055  *  
46056  
46057  */
46058
46059  
46060 /**
46061  * @class Roo.form.HtmlEditor.ToolbarContext
46062  * Context Toolbar
46063  * 
46064  * Usage:
46065  *
46066  new Roo.form.HtmlEditor({
46067     ....
46068     toolbars : [
46069         { xtype: 'ToolbarStandard', styles : {} }
46070         { xtype: 'ToolbarContext', disable : {} }
46071     ]
46072 })
46073
46074      
46075  * 
46076  * @config : {Object} disable List of elements to disable.. (not done yet.)
46077  * @config : {Object} styles  Map of styles available.
46078  * 
46079  */
46080
46081 Roo.form.HtmlEditor.ToolbarContext = function(config)
46082 {
46083     
46084     Roo.apply(this, config);
46085     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46086     // dont call parent... till later.
46087     this.styles = this.styles || {};
46088 }
46089
46090  
46091
46092 Roo.form.HtmlEditor.ToolbarContext.types = {
46093     'IMG' : {
46094         width : {
46095             title: "Width",
46096             width: 40
46097         },
46098         height:  {
46099             title: "Height",
46100             width: 40
46101         },
46102         align: {
46103             title: "Align",
46104             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46105             width : 80
46106             
46107         },
46108         border: {
46109             title: "Border",
46110             width: 40
46111         },
46112         alt: {
46113             title: "Alt",
46114             width: 120
46115         },
46116         src : {
46117             title: "Src",
46118             width: 220
46119         }
46120         
46121     },
46122     'A' : {
46123         name : {
46124             title: "Name",
46125             width: 50
46126         },
46127         target:  {
46128             title: "Target",
46129             width: 120
46130         },
46131         href:  {
46132             title: "Href",
46133             width: 220
46134         } // border?
46135         
46136     },
46137     'TABLE' : {
46138         rows : {
46139             title: "Rows",
46140             width: 20
46141         },
46142         cols : {
46143             title: "Cols",
46144             width: 20
46145         },
46146         width : {
46147             title: "Width",
46148             width: 40
46149         },
46150         height : {
46151             title: "Height",
46152             width: 40
46153         },
46154         border : {
46155             title: "Border",
46156             width: 20
46157         }
46158     },
46159     'TD' : {
46160         width : {
46161             title: "Width",
46162             width: 40
46163         },
46164         height : {
46165             title: "Height",
46166             width: 40
46167         },   
46168         align: {
46169             title: "Align",
46170             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46171             width: 80
46172         },
46173         valign: {
46174             title: "Valign",
46175             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46176             width: 80
46177         },
46178         colspan: {
46179             title: "Colspan",
46180             width: 20
46181             
46182         },
46183          'font-family'  : {
46184             title : "Font",
46185             style : 'fontFamily',
46186             displayField: 'display',
46187             optname : 'font-family',
46188             width: 140
46189         }
46190     },
46191     'INPUT' : {
46192         name : {
46193             title: "name",
46194             width: 120
46195         },
46196         value : {
46197             title: "Value",
46198             width: 120
46199         },
46200         width : {
46201             title: "Width",
46202             width: 40
46203         }
46204     },
46205     'LABEL' : {
46206         'for' : {
46207             title: "For",
46208             width: 120
46209         }
46210     },
46211     'TEXTAREA' : {
46212           name : {
46213             title: "name",
46214             width: 120
46215         },
46216         rows : {
46217             title: "Rows",
46218             width: 20
46219         },
46220         cols : {
46221             title: "Cols",
46222             width: 20
46223         }
46224     },
46225     'SELECT' : {
46226         name : {
46227             title: "name",
46228             width: 120
46229         },
46230         selectoptions : {
46231             title: "Options",
46232             width: 200
46233         }
46234     },
46235     
46236     // should we really allow this??
46237     // should this just be 
46238     'BODY' : {
46239         title : {
46240             title: "Title",
46241             width: 200,
46242             disabled : true
46243         }
46244     },
46245     'SPAN' : {
46246         'font-family'  : {
46247             title : "Font",
46248             style : 'fontFamily',
46249             displayField: 'display',
46250             optname : 'font-family',
46251             width: 140
46252         }
46253     },
46254     'DIV' : {
46255         'font-family'  : {
46256             title : "Font",
46257             style : 'fontFamily',
46258             displayField: 'display',
46259             optname : 'font-family',
46260             width: 140
46261         }
46262     },
46263      'P' : {
46264         'font-family'  : {
46265             title : "Font",
46266             style : 'fontFamily',
46267             displayField: 'display',
46268             optname : 'font-family',
46269             width: 140
46270         }
46271     },
46272     
46273     '*' : {
46274         // empty..
46275     }
46276
46277 };
46278
46279 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46280 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46281
46282 Roo.form.HtmlEditor.ToolbarContext.options = {
46283         'font-family'  : [ 
46284                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46285                 [ 'Courier New', 'Courier New'],
46286                 [ 'Tahoma', 'Tahoma'],
46287                 [ 'Times New Roman,serif', 'Times'],
46288                 [ 'Verdana','Verdana' ]
46289         ]
46290 };
46291
46292 // fixme - these need to be configurable..
46293  
46294
46295 //Roo.form.HtmlEditor.ToolbarContext.types
46296
46297
46298 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46299     
46300     tb: false,
46301     
46302     rendered: false,
46303     
46304     editor : false,
46305     editorcore : false,
46306     /**
46307      * @cfg {Object} disable  List of toolbar elements to disable
46308          
46309      */
46310     disable : false,
46311     /**
46312      * @cfg {Object} styles List of styles 
46313      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46314      *
46315      * These must be defined in the page, so they get rendered correctly..
46316      * .headline { }
46317      * TD.underline { }
46318      * 
46319      */
46320     styles : false,
46321     
46322     options: false,
46323     
46324     toolbars : false,
46325     
46326     init : function(editor)
46327     {
46328         this.editor = editor;
46329         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46330         var editorcore = this.editorcore;
46331         
46332         var fid = editorcore.frameId;
46333         var etb = this;
46334         function btn(id, toggle, handler){
46335             var xid = fid + '-'+ id ;
46336             return {
46337                 id : xid,
46338                 cmd : id,
46339                 cls : 'x-btn-icon x-edit-'+id,
46340                 enableToggle:toggle !== false,
46341                 scope: editorcore, // was editor...
46342                 handler:handler||editorcore.relayBtnCmd,
46343                 clickEvent:'mousedown',
46344                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46345                 tabIndex:-1
46346             };
46347         }
46348         // create a new element.
46349         var wdiv = editor.wrap.createChild({
46350                 tag: 'div'
46351             }, editor.wrap.dom.firstChild.nextSibling, true);
46352         
46353         // can we do this more than once??
46354         
46355          // stop form submits
46356       
46357  
46358         // disable everything...
46359         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46360         this.toolbars = {};
46361            
46362         for (var i in  ty) {
46363           
46364             this.toolbars[i] = this.buildToolbar(ty[i],i);
46365         }
46366         this.tb = this.toolbars.BODY;
46367         this.tb.el.show();
46368         this.buildFooter();
46369         this.footer.show();
46370         editor.on('hide', function( ) { this.footer.hide() }, this);
46371         editor.on('show', function( ) { this.footer.show() }, this);
46372         
46373          
46374         this.rendered = true;
46375         
46376         // the all the btns;
46377         editor.on('editorevent', this.updateToolbar, this);
46378         // other toolbars need to implement this..
46379         //editor.on('editmodechange', this.updateToolbar, this);
46380     },
46381     
46382     
46383     
46384     /**
46385      * Protected method that will not generally be called directly. It triggers
46386      * a toolbar update by reading the markup state of the current selection in the editor.
46387      *
46388      * Note you can force an update by calling on('editorevent', scope, false)
46389      */
46390     updateToolbar: function(editor,ev,sel){
46391
46392         //Roo.log(ev);
46393         // capture mouse up - this is handy for selecting images..
46394         // perhaps should go somewhere else...
46395         if(!this.editorcore.activated){
46396              this.editor.onFirstFocus();
46397             return;
46398         }
46399         
46400         
46401         
46402         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46403         // selectNode - might want to handle IE?
46404         if (ev &&
46405             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46406             ev.target && ev.target.tagName == 'IMG') {
46407             // they have click on an image...
46408             // let's see if we can change the selection...
46409             sel = ev.target;
46410          
46411               var nodeRange = sel.ownerDocument.createRange();
46412             try {
46413                 nodeRange.selectNode(sel);
46414             } catch (e) {
46415                 nodeRange.selectNodeContents(sel);
46416             }
46417             //nodeRange.collapse(true);
46418             var s = this.editorcore.win.getSelection();
46419             s.removeAllRanges();
46420             s.addRange(nodeRange);
46421         }  
46422         
46423       
46424         var updateFooter = sel ? false : true;
46425         
46426         
46427         var ans = this.editorcore.getAllAncestors();
46428         
46429         // pick
46430         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46431         
46432         if (!sel) { 
46433             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46434             sel = sel ? sel : this.editorcore.doc.body;
46435             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46436             
46437         }
46438         // pick a menu that exists..
46439         var tn = sel.tagName.toUpperCase();
46440         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46441         
46442         tn = sel.tagName.toUpperCase();
46443         
46444         var lastSel = this.tb.selectedNode;
46445         
46446         this.tb.selectedNode = sel;
46447         
46448         // if current menu does not match..
46449         
46450         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46451                 
46452             this.tb.el.hide();
46453             ///console.log("show: " + tn);
46454             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46455             this.tb.el.show();
46456             // update name
46457             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46458             
46459             
46460             // update attributes
46461             if (this.tb.fields) {
46462                 this.tb.fields.each(function(e) {
46463                     if (e.stylename) {
46464                         e.setValue(sel.style[e.stylename]);
46465                         return;
46466                     } 
46467                    e.setValue(sel.getAttribute(e.attrname));
46468                 });
46469             }
46470             
46471             var hasStyles = false;
46472             for(var i in this.styles) {
46473                 hasStyles = true;
46474                 break;
46475             }
46476             
46477             // update styles
46478             if (hasStyles) { 
46479                 var st = this.tb.fields.item(0);
46480                 
46481                 st.store.removeAll();
46482                
46483                 
46484                 var cn = sel.className.split(/\s+/);
46485                 
46486                 var avs = [];
46487                 if (this.styles['*']) {
46488                     
46489                     Roo.each(this.styles['*'], function(v) {
46490                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46491                     });
46492                 }
46493                 if (this.styles[tn]) { 
46494                     Roo.each(this.styles[tn], function(v) {
46495                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46496                     });
46497                 }
46498                 
46499                 st.store.loadData(avs);
46500                 st.collapse();
46501                 st.setValue(cn);
46502             }
46503             // flag our selected Node.
46504             this.tb.selectedNode = sel;
46505            
46506            
46507             Roo.menu.MenuMgr.hideAll();
46508
46509         }
46510         
46511         if (!updateFooter) {
46512             //this.footDisp.dom.innerHTML = ''; 
46513             return;
46514         }
46515         // update the footer
46516         //
46517         var html = '';
46518         
46519         this.footerEls = ans.reverse();
46520         Roo.each(this.footerEls, function(a,i) {
46521             if (!a) { return; }
46522             html += html.length ? ' &gt; '  :  '';
46523             
46524             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46525             
46526         });
46527        
46528         // 
46529         var sz = this.footDisp.up('td').getSize();
46530         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46531         this.footDisp.dom.style.marginLeft = '5px';
46532         
46533         this.footDisp.dom.style.overflow = 'hidden';
46534         
46535         this.footDisp.dom.innerHTML = html;
46536             
46537         //this.editorsyncValue();
46538     },
46539      
46540     
46541    
46542        
46543     // private
46544     onDestroy : function(){
46545         if(this.rendered){
46546             
46547             this.tb.items.each(function(item){
46548                 if(item.menu){
46549                     item.menu.removeAll();
46550                     if(item.menu.el){
46551                         item.menu.el.destroy();
46552                     }
46553                 }
46554                 item.destroy();
46555             });
46556              
46557         }
46558     },
46559     onFirstFocus: function() {
46560         // need to do this for all the toolbars..
46561         this.tb.items.each(function(item){
46562            item.enable();
46563         });
46564     },
46565     buildToolbar: function(tlist, nm)
46566     {
46567         var editor = this.editor;
46568         var editorcore = this.editorcore;
46569          // create a new element.
46570         var wdiv = editor.wrap.createChild({
46571                 tag: 'div'
46572             }, editor.wrap.dom.firstChild.nextSibling, true);
46573         
46574        
46575         var tb = new Roo.Toolbar(wdiv);
46576         // add the name..
46577         
46578         tb.add(nm+ ":&nbsp;");
46579         
46580         var styles = [];
46581         for(var i in this.styles) {
46582             styles.push(i);
46583         }
46584         
46585         // styles...
46586         if (styles && styles.length) {
46587             
46588             // this needs a multi-select checkbox...
46589             tb.addField( new Roo.form.ComboBox({
46590                 store: new Roo.data.SimpleStore({
46591                     id : 'val',
46592                     fields: ['val', 'selected'],
46593                     data : [] 
46594                 }),
46595                 name : '-roo-edit-className',
46596                 attrname : 'className',
46597                 displayField: 'val',
46598                 typeAhead: false,
46599                 mode: 'local',
46600                 editable : false,
46601                 triggerAction: 'all',
46602                 emptyText:'Select Style',
46603                 selectOnFocus:true,
46604                 width: 130,
46605                 listeners : {
46606                     'select': function(c, r, i) {
46607                         // initial support only for on class per el..
46608                         tb.selectedNode.className =  r ? r.get('val') : '';
46609                         editorcore.syncValue();
46610                     }
46611                 }
46612     
46613             }));
46614         }
46615         
46616         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46617         var tbops = tbc.options;
46618         
46619         for (var i in tlist) {
46620             
46621             var item = tlist[i];
46622             tb.add(item.title + ":&nbsp;");
46623             
46624             
46625             //optname == used so you can configure the options available..
46626             var opts = item.opts ? item.opts : false;
46627             if (item.optname) {
46628                 opts = tbops[item.optname];
46629            
46630             }
46631             
46632             if (opts) {
46633                 // opts == pulldown..
46634                 tb.addField( new Roo.form.ComboBox({
46635                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46636                         id : 'val',
46637                         fields: ['val', 'display'],
46638                         data : opts  
46639                     }),
46640                     name : '-roo-edit-' + i,
46641                     attrname : i,
46642                     stylename : item.style ? item.style : false,
46643                     displayField: item.displayField ? item.displayField : 'val',
46644                     valueField :  'val',
46645                     typeAhead: false,
46646                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46647                     editable : false,
46648                     triggerAction: 'all',
46649                     emptyText:'Select',
46650                     selectOnFocus:true,
46651                     width: item.width ? item.width  : 130,
46652                     listeners : {
46653                         'select': function(c, r, i) {
46654                             if (c.stylename) {
46655                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46656                                 return;
46657                             }
46658                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46659                         }
46660                     }
46661
46662                 }));
46663                 continue;
46664                     
46665                  
46666                 
46667                 tb.addField( new Roo.form.TextField({
46668                     name: i,
46669                     width: 100,
46670                     //allowBlank:false,
46671                     value: ''
46672                 }));
46673                 continue;
46674             }
46675             tb.addField( new Roo.form.TextField({
46676                 name: '-roo-edit-' + i,
46677                 attrname : i,
46678                 
46679                 width: item.width,
46680                 //allowBlank:true,
46681                 value: '',
46682                 listeners: {
46683                     'change' : function(f, nv, ov) {
46684                         tb.selectedNode.setAttribute(f.attrname, nv);
46685                         editorcore.syncValue();
46686                     }
46687                 }
46688             }));
46689              
46690         }
46691         
46692         var _this = this;
46693         
46694         if(nm == 'BODY'){
46695             tb.addSeparator();
46696         
46697             tb.addButton( {
46698                 text: 'Stylesheets',
46699
46700                 listeners : {
46701                     click : function ()
46702                     {
46703                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46704                     }
46705                 }
46706             });
46707         }
46708         
46709         tb.addFill();
46710         tb.addButton( {
46711             text: 'Remove Tag',
46712     
46713             listeners : {
46714                 click : function ()
46715                 {
46716                     // remove
46717                     // undo does not work.
46718                      
46719                     var sn = tb.selectedNode;
46720                     
46721                     var pn = sn.parentNode;
46722                     
46723                     var stn =  sn.childNodes[0];
46724                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46725                     while (sn.childNodes.length) {
46726                         var node = sn.childNodes[0];
46727                         sn.removeChild(node);
46728                         //Roo.log(node);
46729                         pn.insertBefore(node, sn);
46730                         
46731                     }
46732                     pn.removeChild(sn);
46733                     var range = editorcore.createRange();
46734         
46735                     range.setStart(stn,0);
46736                     range.setEnd(en,0); //????
46737                     //range.selectNode(sel);
46738                     
46739                     
46740                     var selection = editorcore.getSelection();
46741                     selection.removeAllRanges();
46742                     selection.addRange(range);
46743                     
46744                     
46745                     
46746                     //_this.updateToolbar(null, null, pn);
46747                     _this.updateToolbar(null, null, null);
46748                     _this.footDisp.dom.innerHTML = ''; 
46749                 }
46750             }
46751             
46752                     
46753                 
46754             
46755         });
46756         
46757         
46758         tb.el.on('click', function(e){
46759             e.preventDefault(); // what does this do?
46760         });
46761         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46762         tb.el.hide();
46763         tb.name = nm;
46764         // dont need to disable them... as they will get hidden
46765         return tb;
46766          
46767         
46768     },
46769     buildFooter : function()
46770     {
46771         
46772         var fel = this.editor.wrap.createChild();
46773         this.footer = new Roo.Toolbar(fel);
46774         // toolbar has scrolly on left / right?
46775         var footDisp= new Roo.Toolbar.Fill();
46776         var _t = this;
46777         this.footer.add(
46778             {
46779                 text : '&lt;',
46780                 xtype: 'Button',
46781                 handler : function() {
46782                     _t.footDisp.scrollTo('left',0,true)
46783                 }
46784             }
46785         );
46786         this.footer.add( footDisp );
46787         this.footer.add( 
46788             {
46789                 text : '&gt;',
46790                 xtype: 'Button',
46791                 handler : function() {
46792                     // no animation..
46793                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46794                 }
46795             }
46796         );
46797         var fel = Roo.get(footDisp.el);
46798         fel.addClass('x-editor-context');
46799         this.footDispWrap = fel; 
46800         this.footDispWrap.overflow  = 'hidden';
46801         
46802         this.footDisp = fel.createChild();
46803         this.footDispWrap.on('click', this.onContextClick, this)
46804         
46805         
46806     },
46807     onContextClick : function (ev,dom)
46808     {
46809         ev.preventDefault();
46810         var  cn = dom.className;
46811         //Roo.log(cn);
46812         if (!cn.match(/x-ed-loc-/)) {
46813             return;
46814         }
46815         var n = cn.split('-').pop();
46816         var ans = this.footerEls;
46817         var sel = ans[n];
46818         
46819          // pick
46820         var range = this.editorcore.createRange();
46821         
46822         range.selectNodeContents(sel);
46823         //range.selectNode(sel);
46824         
46825         
46826         var selection = this.editorcore.getSelection();
46827         selection.removeAllRanges();
46828         selection.addRange(range);
46829         
46830         
46831         
46832         this.updateToolbar(null, null, sel);
46833         
46834         
46835     }
46836     
46837     
46838     
46839     
46840     
46841 });
46842
46843
46844
46845
46846
46847 /*
46848  * Based on:
46849  * Ext JS Library 1.1.1
46850  * Copyright(c) 2006-2007, Ext JS, LLC.
46851  *
46852  * Originally Released Under LGPL - original licence link has changed is not relivant.
46853  *
46854  * Fork - LGPL
46855  * <script type="text/javascript">
46856  */
46857  
46858 /**
46859  * @class Roo.form.BasicForm
46860  * @extends Roo.util.Observable
46861  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46862  * @constructor
46863  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46864  * @param {Object} config Configuration options
46865  */
46866 Roo.form.BasicForm = function(el, config){
46867     this.allItems = [];
46868     this.childForms = [];
46869     Roo.apply(this, config);
46870     /*
46871      * The Roo.form.Field items in this form.
46872      * @type MixedCollection
46873      */
46874      
46875      
46876     this.items = new Roo.util.MixedCollection(false, function(o){
46877         return o.id || (o.id = Roo.id());
46878     });
46879     this.addEvents({
46880         /**
46881          * @event beforeaction
46882          * Fires before any action is performed. Return false to cancel the action.
46883          * @param {Form} this
46884          * @param {Action} action The action to be performed
46885          */
46886         beforeaction: true,
46887         /**
46888          * @event actionfailed
46889          * Fires when an action fails.
46890          * @param {Form} this
46891          * @param {Action} action The action that failed
46892          */
46893         actionfailed : true,
46894         /**
46895          * @event actioncomplete
46896          * Fires when an action is completed.
46897          * @param {Form} this
46898          * @param {Action} action The action that completed
46899          */
46900         actioncomplete : true
46901     });
46902     if(el){
46903         this.initEl(el);
46904     }
46905     Roo.form.BasicForm.superclass.constructor.call(this);
46906     
46907     Roo.form.BasicForm.popover.apply();
46908 };
46909
46910 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46911     /**
46912      * @cfg {String} method
46913      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46914      */
46915     /**
46916      * @cfg {DataReader} reader
46917      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46918      * This is optional as there is built-in support for processing JSON.
46919      */
46920     /**
46921      * @cfg {DataReader} errorReader
46922      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46923      * This is completely optional as there is built-in support for processing JSON.
46924      */
46925     /**
46926      * @cfg {String} url
46927      * The URL to use for form actions if one isn't supplied in the action options.
46928      */
46929     /**
46930      * @cfg {Boolean} fileUpload
46931      * Set to true if this form is a file upload.
46932      */
46933      
46934     /**
46935      * @cfg {Object} baseParams
46936      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46937      */
46938      /**
46939      
46940     /**
46941      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46942      */
46943     timeout: 30,
46944
46945     // private
46946     activeAction : null,
46947
46948     /**
46949      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46950      * or setValues() data instead of when the form was first created.
46951      */
46952     trackResetOnLoad : false,
46953     
46954     
46955     /**
46956      * childForms - used for multi-tab forms
46957      * @type {Array}
46958      */
46959     childForms : false,
46960     
46961     /**
46962      * allItems - full list of fields.
46963      * @type {Array}
46964      */
46965     allItems : false,
46966     
46967     /**
46968      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46969      * element by passing it or its id or mask the form itself by passing in true.
46970      * @type Mixed
46971      */
46972     waitMsgTarget : false,
46973     
46974     /**
46975      * @type Boolean
46976      */
46977     disableMask : false,
46978     
46979     /**
46980      * @cfg {Boolean} errorMask (true|false) default false
46981      */
46982     errorMask : false,
46983     
46984     /**
46985      * @cfg {Number} maskOffset Default 100
46986      */
46987     maskOffset : 100,
46988
46989     // private
46990     initEl : function(el){
46991         this.el = Roo.get(el);
46992         this.id = this.el.id || Roo.id();
46993         this.el.on('submit', this.onSubmit, this);
46994         this.el.addClass('x-form');
46995     },
46996
46997     // private
46998     onSubmit : function(e){
46999         e.stopEvent();
47000     },
47001
47002     /**
47003      * Returns true if client-side validation on the form is successful.
47004      * @return Boolean
47005      */
47006     isValid : function(){
47007         var valid = true;
47008         var target = false;
47009         this.items.each(function(f){
47010             if(f.validate()){
47011                 return;
47012             }
47013             
47014             valid = false;
47015                 
47016             if(!target && f.el.isVisible(true)){
47017                 target = f;
47018             }
47019         });
47020         
47021         if(this.errorMask && !valid){
47022             Roo.form.BasicForm.popover.mask(this, target);
47023         }
47024         
47025         return valid;
47026     },
47027
47028     /**
47029      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47030      * @return Boolean
47031      */
47032     isDirty : function(){
47033         var dirty = false;
47034         this.items.each(function(f){
47035            if(f.isDirty()){
47036                dirty = true;
47037                return false;
47038            }
47039         });
47040         return dirty;
47041     },
47042     
47043     /**
47044      * Returns true if any fields in this form have changed since their original load. (New version)
47045      * @return Boolean
47046      */
47047     
47048     hasChanged : function()
47049     {
47050         var dirty = false;
47051         this.items.each(function(f){
47052            if(f.hasChanged()){
47053                dirty = true;
47054                return false;
47055            }
47056         });
47057         return dirty;
47058         
47059     },
47060     /**
47061      * Resets all hasChanged to 'false' -
47062      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47063      * So hasChanged storage is only to be used for this purpose
47064      * @return Boolean
47065      */
47066     resetHasChanged : function()
47067     {
47068         this.items.each(function(f){
47069            f.resetHasChanged();
47070         });
47071         
47072     },
47073     
47074     
47075     /**
47076      * Performs a predefined action (submit or load) or custom actions you define on this form.
47077      * @param {String} actionName The name of the action type
47078      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47079      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47080      * accept other config options):
47081      * <pre>
47082 Property          Type             Description
47083 ----------------  ---------------  ----------------------------------------------------------------------------------
47084 url               String           The url for the action (defaults to the form's url)
47085 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47086 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47087 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47088                                    validate the form on the client (defaults to false)
47089      * </pre>
47090      * @return {BasicForm} this
47091      */
47092     doAction : function(action, options){
47093         if(typeof action == 'string'){
47094             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47095         }
47096         if(this.fireEvent('beforeaction', this, action) !== false){
47097             this.beforeAction(action);
47098             action.run.defer(100, action);
47099         }
47100         return this;
47101     },
47102
47103     /**
47104      * Shortcut to do a submit action.
47105      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47106      * @return {BasicForm} this
47107      */
47108     submit : function(options){
47109         this.doAction('submit', options);
47110         return this;
47111     },
47112
47113     /**
47114      * Shortcut to do a load action.
47115      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47116      * @return {BasicForm} this
47117      */
47118     load : function(options){
47119         this.doAction('load', options);
47120         return this;
47121     },
47122
47123     /**
47124      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47125      * @param {Record} record The record to edit
47126      * @return {BasicForm} this
47127      */
47128     updateRecord : function(record){
47129         record.beginEdit();
47130         var fs = record.fields;
47131         fs.each(function(f){
47132             var field = this.findField(f.name);
47133             if(field){
47134                 record.set(f.name, field.getValue());
47135             }
47136         }, this);
47137         record.endEdit();
47138         return this;
47139     },
47140
47141     /**
47142      * Loads an Roo.data.Record into this form.
47143      * @param {Record} record The record to load
47144      * @return {BasicForm} this
47145      */
47146     loadRecord : function(record){
47147         this.setValues(record.data);
47148         return this;
47149     },
47150
47151     // private
47152     beforeAction : function(action){
47153         var o = action.options;
47154         
47155         if(!this.disableMask) {
47156             if(this.waitMsgTarget === true){
47157                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47158             }else if(this.waitMsgTarget){
47159                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47160                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47161             }else {
47162                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47163             }
47164         }
47165         
47166          
47167     },
47168
47169     // private
47170     afterAction : function(action, success){
47171         this.activeAction = null;
47172         var o = action.options;
47173         
47174         if(!this.disableMask) {
47175             if(this.waitMsgTarget === true){
47176                 this.el.unmask();
47177             }else if(this.waitMsgTarget){
47178                 this.waitMsgTarget.unmask();
47179             }else{
47180                 Roo.MessageBox.updateProgress(1);
47181                 Roo.MessageBox.hide();
47182             }
47183         }
47184         
47185         if(success){
47186             if(o.reset){
47187                 this.reset();
47188             }
47189             Roo.callback(o.success, o.scope, [this, action]);
47190             this.fireEvent('actioncomplete', this, action);
47191             
47192         }else{
47193             
47194             // failure condition..
47195             // we have a scenario where updates need confirming.
47196             // eg. if a locking scenario exists..
47197             // we look for { errors : { needs_confirm : true }} in the response.
47198             if (
47199                 (typeof(action.result) != 'undefined')  &&
47200                 (typeof(action.result.errors) != 'undefined')  &&
47201                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47202            ){
47203                 var _t = this;
47204                 Roo.MessageBox.confirm(
47205                     "Change requires confirmation",
47206                     action.result.errorMsg,
47207                     function(r) {
47208                         if (r != 'yes') {
47209                             return;
47210                         }
47211                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47212                     }
47213                     
47214                 );
47215                 
47216                 
47217                 
47218                 return;
47219             }
47220             
47221             Roo.callback(o.failure, o.scope, [this, action]);
47222             // show an error message if no failed handler is set..
47223             if (!this.hasListener('actionfailed')) {
47224                 Roo.MessageBox.alert("Error",
47225                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47226                         action.result.errorMsg :
47227                         "Saving Failed, please check your entries or try again"
47228                 );
47229             }
47230             
47231             this.fireEvent('actionfailed', this, action);
47232         }
47233         
47234     },
47235
47236     /**
47237      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47238      * @param {String} id The value to search for
47239      * @return Field
47240      */
47241     findField : function(id){
47242         var field = this.items.get(id);
47243         if(!field){
47244             this.items.each(function(f){
47245                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47246                     field = f;
47247                     return false;
47248                 }
47249             });
47250         }
47251         return field || null;
47252     },
47253
47254     /**
47255      * Add a secondary form to this one, 
47256      * Used to provide tabbed forms. One form is primary, with hidden values 
47257      * which mirror the elements from the other forms.
47258      * 
47259      * @param {Roo.form.Form} form to add.
47260      * 
47261      */
47262     addForm : function(form)
47263     {
47264        
47265         if (this.childForms.indexOf(form) > -1) {
47266             // already added..
47267             return;
47268         }
47269         this.childForms.push(form);
47270         var n = '';
47271         Roo.each(form.allItems, function (fe) {
47272             
47273             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47274             if (this.findField(n)) { // already added..
47275                 return;
47276             }
47277             var add = new Roo.form.Hidden({
47278                 name : n
47279             });
47280             add.render(this.el);
47281             
47282             this.add( add );
47283         }, this);
47284         
47285     },
47286     /**
47287      * Mark fields in this form invalid in bulk.
47288      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47289      * @return {BasicForm} this
47290      */
47291     markInvalid : function(errors){
47292         if(errors instanceof Array){
47293             for(var i = 0, len = errors.length; i < len; i++){
47294                 var fieldError = errors[i];
47295                 var f = this.findField(fieldError.id);
47296                 if(f){
47297                     f.markInvalid(fieldError.msg);
47298                 }
47299             }
47300         }else{
47301             var field, id;
47302             for(id in errors){
47303                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47304                     field.markInvalid(errors[id]);
47305                 }
47306             }
47307         }
47308         Roo.each(this.childForms || [], function (f) {
47309             f.markInvalid(errors);
47310         });
47311         
47312         return this;
47313     },
47314
47315     /**
47316      * Set values for fields in this form in bulk.
47317      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47318      * @return {BasicForm} this
47319      */
47320     setValues : function(values){
47321         if(values instanceof Array){ // array of objects
47322             for(var i = 0, len = values.length; i < len; i++){
47323                 var v = values[i];
47324                 var f = this.findField(v.id);
47325                 if(f){
47326                     f.setValue(v.value);
47327                     if(this.trackResetOnLoad){
47328                         f.originalValue = f.getValue();
47329                     }
47330                 }
47331             }
47332         }else{ // object hash
47333             var field, id;
47334             for(id in values){
47335                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47336                     
47337                     if (field.setFromData && 
47338                         field.valueField && 
47339                         field.displayField &&
47340                         // combos' with local stores can 
47341                         // be queried via setValue()
47342                         // to set their value..
47343                         (field.store && !field.store.isLocal)
47344                         ) {
47345                         // it's a combo
47346                         var sd = { };
47347                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47348                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47349                         field.setFromData(sd);
47350                         
47351                     } else {
47352                         field.setValue(values[id]);
47353                     }
47354                     
47355                     
47356                     if(this.trackResetOnLoad){
47357                         field.originalValue = field.getValue();
47358                     }
47359                 }
47360             }
47361         }
47362         this.resetHasChanged();
47363         
47364         
47365         Roo.each(this.childForms || [], function (f) {
47366             f.setValues(values);
47367             f.resetHasChanged();
47368         });
47369                 
47370         return this;
47371     },
47372  
47373     /**
47374      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47375      * they are returned as an array.
47376      * @param {Boolean} asString
47377      * @return {Object}
47378      */
47379     getValues : function(asString){
47380         if (this.childForms) {
47381             // copy values from the child forms
47382             Roo.each(this.childForms, function (f) {
47383                 this.setValues(f.getValues());
47384             }, this);
47385         }
47386         
47387         // use formdata
47388         if (typeof(FormData) != 'undefined' && asString !== true) {
47389             var fd = (new FormData(this.el.dom)).entries();
47390             var ret = {};
47391             var ent = fd.next();
47392             while (!ent.done) {
47393                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47394                 ent = fd.next();
47395             };
47396             return ret;
47397         }
47398         
47399         
47400         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47401         if(asString === true){
47402             return fs;
47403         }
47404         return Roo.urlDecode(fs);
47405     },
47406     
47407     /**
47408      * Returns the fields in this form as an object with key/value pairs. 
47409      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47410      * @return {Object}
47411      */
47412     getFieldValues : function(with_hidden)
47413     {
47414         if (this.childForms) {
47415             // copy values from the child forms
47416             // should this call getFieldValues - probably not as we do not currently copy
47417             // hidden fields when we generate..
47418             Roo.each(this.childForms, function (f) {
47419                 this.setValues(f.getValues());
47420             }, this);
47421         }
47422         
47423         var ret = {};
47424         this.items.each(function(f){
47425             if (!f.getName()) {
47426                 return;
47427             }
47428             var v = f.getValue();
47429             if (f.inputType =='radio') {
47430                 if (typeof(ret[f.getName()]) == 'undefined') {
47431                     ret[f.getName()] = ''; // empty..
47432                 }
47433                 
47434                 if (!f.el.dom.checked) {
47435                     return;
47436                     
47437                 }
47438                 v = f.el.dom.value;
47439                 
47440             }
47441             
47442             // not sure if this supported any more..
47443             if ((typeof(v) == 'object') && f.getRawValue) {
47444                 v = f.getRawValue() ; // dates..
47445             }
47446             // combo boxes where name != hiddenName...
47447             if (f.name != f.getName()) {
47448                 ret[f.name] = f.getRawValue();
47449             }
47450             ret[f.getName()] = v;
47451         });
47452         
47453         return ret;
47454     },
47455
47456     /**
47457      * Clears all invalid messages in this form.
47458      * @return {BasicForm} this
47459      */
47460     clearInvalid : function(){
47461         this.items.each(function(f){
47462            f.clearInvalid();
47463         });
47464         
47465         Roo.each(this.childForms || [], function (f) {
47466             f.clearInvalid();
47467         });
47468         
47469         
47470         return this;
47471     },
47472
47473     /**
47474      * Resets this form.
47475      * @return {BasicForm} this
47476      */
47477     reset : function(){
47478         this.items.each(function(f){
47479             f.reset();
47480         });
47481         
47482         Roo.each(this.childForms || [], function (f) {
47483             f.reset();
47484         });
47485         this.resetHasChanged();
47486         
47487         return this;
47488     },
47489
47490     /**
47491      * Add Roo.form components to this form.
47492      * @param {Field} field1
47493      * @param {Field} field2 (optional)
47494      * @param {Field} etc (optional)
47495      * @return {BasicForm} this
47496      */
47497     add : function(){
47498         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47499         return this;
47500     },
47501
47502
47503     /**
47504      * Removes a field from the items collection (does NOT remove its markup).
47505      * @param {Field} field
47506      * @return {BasicForm} this
47507      */
47508     remove : function(field){
47509         this.items.remove(field);
47510         return this;
47511     },
47512
47513     /**
47514      * Looks at the fields in this form, checks them for an id attribute,
47515      * and calls applyTo on the existing dom element with that id.
47516      * @return {BasicForm} this
47517      */
47518     render : function(){
47519         this.items.each(function(f){
47520             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47521                 f.applyTo(f.id);
47522             }
47523         });
47524         return this;
47525     },
47526
47527     /**
47528      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47529      * @param {Object} values
47530      * @return {BasicForm} this
47531      */
47532     applyToFields : function(o){
47533         this.items.each(function(f){
47534            Roo.apply(f, o);
47535         });
47536         return this;
47537     },
47538
47539     /**
47540      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47541      * @param {Object} values
47542      * @return {BasicForm} this
47543      */
47544     applyIfToFields : function(o){
47545         this.items.each(function(f){
47546            Roo.applyIf(f, o);
47547         });
47548         return this;
47549     }
47550 });
47551
47552 // back compat
47553 Roo.BasicForm = Roo.form.BasicForm;
47554
47555 Roo.apply(Roo.form.BasicForm, {
47556     
47557     popover : {
47558         
47559         padding : 5,
47560         
47561         isApplied : false,
47562         
47563         isMasked : false,
47564         
47565         form : false,
47566         
47567         target : false,
47568         
47569         intervalID : false,
47570         
47571         maskEl : false,
47572         
47573         apply : function()
47574         {
47575             if(this.isApplied){
47576                 return;
47577             }
47578             
47579             this.maskEl = {
47580                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
47581                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
47582                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
47583                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
47584             };
47585             
47586             this.maskEl.top.enableDisplayMode("block");
47587             this.maskEl.left.enableDisplayMode("block");
47588             this.maskEl.bottom.enableDisplayMode("block");
47589             this.maskEl.right.enableDisplayMode("block");
47590             
47591             Roo.get(document.body).on('click', function(){
47592                 this.unmask();
47593             }, this);
47594             
47595             Roo.get(document.body).on('touchstart', function(){
47596                 this.unmask();
47597             }, this);
47598             
47599             this.isApplied = true
47600         },
47601         
47602         mask : function(form, target)
47603         {
47604             this.form = form;
47605             
47606             this.target = target;
47607             
47608             if(!this.form.errorMask || !target.el){
47609                 return;
47610             }
47611             
47612             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
47613             
47614             var ot = this.target.el.calcOffsetsTo(scrollable);
47615             
47616             var scrollTo = ot[1] - this.form.maskOffset;
47617             
47618             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
47619             
47620             scrollable.scrollTo('top', scrollTo);
47621             
47622             var el = this.target.wrap || this.target.el;
47623             
47624             var box = el.getBox();
47625             
47626             this.maskEl.top.setStyle('position', 'absolute');
47627             this.maskEl.top.setStyle('z-index', 10000);
47628             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
47629             this.maskEl.top.setLeft(0);
47630             this.maskEl.top.setTop(0);
47631             this.maskEl.top.show();
47632             
47633             this.maskEl.left.setStyle('position', 'absolute');
47634             this.maskEl.left.setStyle('z-index', 10000);
47635             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
47636             this.maskEl.left.setLeft(0);
47637             this.maskEl.left.setTop(box.y - this.padding);
47638             this.maskEl.left.show();
47639
47640             this.maskEl.bottom.setStyle('position', 'absolute');
47641             this.maskEl.bottom.setStyle('z-index', 10000);
47642             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
47643             this.maskEl.bottom.setLeft(0);
47644             this.maskEl.bottom.setTop(box.bottom + this.padding);
47645             this.maskEl.bottom.show();
47646
47647             this.maskEl.right.setStyle('position', 'absolute');
47648             this.maskEl.right.setStyle('z-index', 10000);
47649             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
47650             this.maskEl.right.setLeft(box.right + this.padding);
47651             this.maskEl.right.setTop(box.y - this.padding);
47652             this.maskEl.right.show();
47653
47654             this.intervalID = window.setInterval(function() {
47655                 Roo.form.BasicForm.popover.unmask();
47656             }, 10000);
47657
47658             window.onwheel = function(){ return false;};
47659             
47660             (function(){ this.isMasked = true; }).defer(500, this);
47661             
47662         },
47663         
47664         unmask : function()
47665         {
47666             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
47667                 return;
47668             }
47669             
47670             this.maskEl.top.setStyle('position', 'absolute');
47671             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
47672             this.maskEl.top.hide();
47673
47674             this.maskEl.left.setStyle('position', 'absolute');
47675             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
47676             this.maskEl.left.hide();
47677
47678             this.maskEl.bottom.setStyle('position', 'absolute');
47679             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
47680             this.maskEl.bottom.hide();
47681
47682             this.maskEl.right.setStyle('position', 'absolute');
47683             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
47684             this.maskEl.right.hide();
47685             
47686             window.onwheel = function(){ return true;};
47687             
47688             if(this.intervalID){
47689                 window.clearInterval(this.intervalID);
47690                 this.intervalID = false;
47691             }
47692             
47693             this.isMasked = false;
47694             
47695         }
47696         
47697     }
47698     
47699 });/*
47700  * Based on:
47701  * Ext JS Library 1.1.1
47702  * Copyright(c) 2006-2007, Ext JS, LLC.
47703  *
47704  * Originally Released Under LGPL - original licence link has changed is not relivant.
47705  *
47706  * Fork - LGPL
47707  * <script type="text/javascript">
47708  */
47709
47710 /**
47711  * @class Roo.form.Form
47712  * @extends Roo.form.BasicForm
47713  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47714  * @constructor
47715  * @param {Object} config Configuration options
47716  */
47717 Roo.form.Form = function(config){
47718     var xitems =  [];
47719     if (config.items) {
47720         xitems = config.items;
47721         delete config.items;
47722     }
47723    
47724     
47725     Roo.form.Form.superclass.constructor.call(this, null, config);
47726     this.url = this.url || this.action;
47727     if(!this.root){
47728         this.root = new Roo.form.Layout(Roo.applyIf({
47729             id: Roo.id()
47730         }, config));
47731     }
47732     this.active = this.root;
47733     /**
47734      * Array of all the buttons that have been added to this form via {@link addButton}
47735      * @type Array
47736      */
47737     this.buttons = [];
47738     this.allItems = [];
47739     this.addEvents({
47740         /**
47741          * @event clientvalidation
47742          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47743          * @param {Form} this
47744          * @param {Boolean} valid true if the form has passed client-side validation
47745          */
47746         clientvalidation: true,
47747         /**
47748          * @event rendered
47749          * Fires when the form is rendered
47750          * @param {Roo.form.Form} form
47751          */
47752         rendered : true
47753     });
47754     
47755     if (this.progressUrl) {
47756             // push a hidden field onto the list of fields..
47757             this.addxtype( {
47758                     xns: Roo.form, 
47759                     xtype : 'Hidden', 
47760                     name : 'UPLOAD_IDENTIFIER' 
47761             });
47762         }
47763         
47764     
47765     Roo.each(xitems, this.addxtype, this);
47766     
47767 };
47768
47769 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47770     /**
47771      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47772      */
47773     /**
47774      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47775      */
47776     /**
47777      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47778      */
47779     buttonAlign:'center',
47780
47781     /**
47782      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47783      */
47784     minButtonWidth:75,
47785
47786     /**
47787      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47788      * This property cascades to child containers if not set.
47789      */
47790     labelAlign:'left',
47791
47792     /**
47793      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47794      * fires a looping event with that state. This is required to bind buttons to the valid
47795      * state using the config value formBind:true on the button.
47796      */
47797     monitorValid : false,
47798
47799     /**
47800      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47801      */
47802     monitorPoll : 200,
47803     
47804     /**
47805      * @cfg {String} progressUrl - Url to return progress data 
47806      */
47807     
47808     progressUrl : false,
47809     /**
47810      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
47811      * sending a formdata with extra parameters - eg uploaded elements.
47812      */
47813     
47814     formData : false,
47815     
47816     /**
47817      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47818      * fields are added and the column is closed. If no fields are passed the column remains open
47819      * until end() is called.
47820      * @param {Object} config The config to pass to the column
47821      * @param {Field} field1 (optional)
47822      * @param {Field} field2 (optional)
47823      * @param {Field} etc (optional)
47824      * @return Column The column container object
47825      */
47826     column : function(c){
47827         var col = new Roo.form.Column(c);
47828         this.start(col);
47829         if(arguments.length > 1){ // duplicate code required because of Opera
47830             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47831             this.end();
47832         }
47833         return col;
47834     },
47835
47836     /**
47837      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47838      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47839      * until end() is called.
47840      * @param {Object} config The config to pass to the fieldset
47841      * @param {Field} field1 (optional)
47842      * @param {Field} field2 (optional)
47843      * @param {Field} etc (optional)
47844      * @return FieldSet The fieldset container object
47845      */
47846     fieldset : function(c){
47847         var fs = new Roo.form.FieldSet(c);
47848         this.start(fs);
47849         if(arguments.length > 1){ // duplicate code required because of Opera
47850             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47851             this.end();
47852         }
47853         return fs;
47854     },
47855
47856     /**
47857      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47858      * fields are added and the container is closed. If no fields are passed the container remains open
47859      * until end() is called.
47860      * @param {Object} config The config to pass to the Layout
47861      * @param {Field} field1 (optional)
47862      * @param {Field} field2 (optional)
47863      * @param {Field} etc (optional)
47864      * @return Layout The container object
47865      */
47866     container : function(c){
47867         var l = new Roo.form.Layout(c);
47868         this.start(l);
47869         if(arguments.length > 1){ // duplicate code required because of Opera
47870             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47871             this.end();
47872         }
47873         return l;
47874     },
47875
47876     /**
47877      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47878      * @param {Object} container A Roo.form.Layout or subclass of Layout
47879      * @return {Form} this
47880      */
47881     start : function(c){
47882         // cascade label info
47883         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47884         this.active.stack.push(c);
47885         c.ownerCt = this.active;
47886         this.active = c;
47887         return this;
47888     },
47889
47890     /**
47891      * Closes the current open container
47892      * @return {Form} this
47893      */
47894     end : function(){
47895         if(this.active == this.root){
47896             return this;
47897         }
47898         this.active = this.active.ownerCt;
47899         return this;
47900     },
47901
47902     /**
47903      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47904      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47905      * as the label of the field.
47906      * @param {Field} field1
47907      * @param {Field} field2 (optional)
47908      * @param {Field} etc. (optional)
47909      * @return {Form} this
47910      */
47911     add : function(){
47912         this.active.stack.push.apply(this.active.stack, arguments);
47913         this.allItems.push.apply(this.allItems,arguments);
47914         var r = [];
47915         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47916             if(a[i].isFormField){
47917                 r.push(a[i]);
47918             }
47919         }
47920         if(r.length > 0){
47921             Roo.form.Form.superclass.add.apply(this, r);
47922         }
47923         return this;
47924     },
47925     
47926
47927     
47928     
47929     
47930      /**
47931      * Find any element that has been added to a form, using it's ID or name
47932      * This can include framesets, columns etc. along with regular fields..
47933      * @param {String} id - id or name to find.
47934      
47935      * @return {Element} e - or false if nothing found.
47936      */
47937     findbyId : function(id)
47938     {
47939         var ret = false;
47940         if (!id) {
47941             return ret;
47942         }
47943         Roo.each(this.allItems, function(f){
47944             if (f.id == id || f.name == id ){
47945                 ret = f;
47946                 return false;
47947             }
47948         });
47949         return ret;
47950     },
47951
47952     
47953     
47954     /**
47955      * Render this form into the passed container. This should only be called once!
47956      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47957      * @return {Form} this
47958      */
47959     render : function(ct)
47960     {
47961         
47962         
47963         
47964         ct = Roo.get(ct);
47965         var o = this.autoCreate || {
47966             tag: 'form',
47967             method : this.method || 'POST',
47968             id : this.id || Roo.id()
47969         };
47970         this.initEl(ct.createChild(o));
47971
47972         this.root.render(this.el);
47973         
47974        
47975              
47976         this.items.each(function(f){
47977             f.render('x-form-el-'+f.id);
47978         });
47979
47980         if(this.buttons.length > 0){
47981             // tables are required to maintain order and for correct IE layout
47982             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47983                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47984                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47985             }}, null, true);
47986             var tr = tb.getElementsByTagName('tr')[0];
47987             for(var i = 0, len = this.buttons.length; i < len; i++) {
47988                 var b = this.buttons[i];
47989                 var td = document.createElement('td');
47990                 td.className = 'x-form-btn-td';
47991                 b.render(tr.appendChild(td));
47992             }
47993         }
47994         if(this.monitorValid){ // initialize after render
47995             this.startMonitoring();
47996         }
47997         this.fireEvent('rendered', this);
47998         return this;
47999     },
48000
48001     /**
48002      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48003      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48004      * object or a valid Roo.DomHelper element config
48005      * @param {Function} handler The function called when the button is clicked
48006      * @param {Object} scope (optional) The scope of the handler function
48007      * @return {Roo.Button}
48008      */
48009     addButton : function(config, handler, scope){
48010         var bc = {
48011             handler: handler,
48012             scope: scope,
48013             minWidth: this.minButtonWidth,
48014             hideParent:true
48015         };
48016         if(typeof config == "string"){
48017             bc.text = config;
48018         }else{
48019             Roo.apply(bc, config);
48020         }
48021         var btn = new Roo.Button(null, bc);
48022         this.buttons.push(btn);
48023         return btn;
48024     },
48025
48026      /**
48027      * Adds a series of form elements (using the xtype property as the factory method.
48028      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48029      * @param {Object} config 
48030      */
48031     
48032     addxtype : function()
48033     {
48034         var ar = Array.prototype.slice.call(arguments, 0);
48035         var ret = false;
48036         for(var i = 0; i < ar.length; i++) {
48037             if (!ar[i]) {
48038                 continue; // skip -- if this happends something invalid got sent, we 
48039                 // should ignore it, as basically that interface element will not show up
48040                 // and that should be pretty obvious!!
48041             }
48042             
48043             if (Roo.form[ar[i].xtype]) {
48044                 ar[i].form = this;
48045                 var fe = Roo.factory(ar[i], Roo.form);
48046                 if (!ret) {
48047                     ret = fe;
48048                 }
48049                 fe.form = this;
48050                 if (fe.store) {
48051                     fe.store.form = this;
48052                 }
48053                 if (fe.isLayout) {  
48054                          
48055                     this.start(fe);
48056                     this.allItems.push(fe);
48057                     if (fe.items && fe.addxtype) {
48058                         fe.addxtype.apply(fe, fe.items);
48059                         delete fe.items;
48060                     }
48061                      this.end();
48062                     continue;
48063                 }
48064                 
48065                 
48066                  
48067                 this.add(fe);
48068               //  console.log('adding ' + ar[i].xtype);
48069             }
48070             if (ar[i].xtype == 'Button') {  
48071                 //console.log('adding button');
48072                 //console.log(ar[i]);
48073                 this.addButton(ar[i]);
48074                 this.allItems.push(fe);
48075                 continue;
48076             }
48077             
48078             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48079                 alert('end is not supported on xtype any more, use items');
48080             //    this.end();
48081             //    //console.log('adding end');
48082             }
48083             
48084         }
48085         return ret;
48086     },
48087     
48088     /**
48089      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48090      * option "monitorValid"
48091      */
48092     startMonitoring : function(){
48093         if(!this.bound){
48094             this.bound = true;
48095             Roo.TaskMgr.start({
48096                 run : this.bindHandler,
48097                 interval : this.monitorPoll || 200,
48098                 scope: this
48099             });
48100         }
48101     },
48102
48103     /**
48104      * Stops monitoring of the valid state of this form
48105      */
48106     stopMonitoring : function(){
48107         this.bound = false;
48108     },
48109
48110     // private
48111     bindHandler : function(){
48112         if(!this.bound){
48113             return false; // stops binding
48114         }
48115         var valid = true;
48116         this.items.each(function(f){
48117             if(!f.isValid(true)){
48118                 valid = false;
48119                 return false;
48120             }
48121         });
48122         for(var i = 0, len = this.buttons.length; i < len; i++){
48123             var btn = this.buttons[i];
48124             if(btn.formBind === true && btn.disabled === valid){
48125                 btn.setDisabled(!valid);
48126             }
48127         }
48128         this.fireEvent('clientvalidation', this, valid);
48129     }
48130     
48131     
48132     
48133     
48134     
48135     
48136     
48137     
48138 });
48139
48140
48141 // back compat
48142 Roo.Form = Roo.form.Form;
48143 /*
48144  * Based on:
48145  * Ext JS Library 1.1.1
48146  * Copyright(c) 2006-2007, Ext JS, LLC.
48147  *
48148  * Originally Released Under LGPL - original licence link has changed is not relivant.
48149  *
48150  * Fork - LGPL
48151  * <script type="text/javascript">
48152  */
48153
48154 // as we use this in bootstrap.
48155 Roo.namespace('Roo.form');
48156  /**
48157  * @class Roo.form.Action
48158  * Internal Class used to handle form actions
48159  * @constructor
48160  * @param {Roo.form.BasicForm} el The form element or its id
48161  * @param {Object} config Configuration options
48162  */
48163
48164  
48165  
48166 // define the action interface
48167 Roo.form.Action = function(form, options){
48168     this.form = form;
48169     this.options = options || {};
48170 };
48171 /**
48172  * Client Validation Failed
48173  * @const 
48174  */
48175 Roo.form.Action.CLIENT_INVALID = 'client';
48176 /**
48177  * Server Validation Failed
48178  * @const 
48179  */
48180 Roo.form.Action.SERVER_INVALID = 'server';
48181  /**
48182  * Connect to Server Failed
48183  * @const 
48184  */
48185 Roo.form.Action.CONNECT_FAILURE = 'connect';
48186 /**
48187  * Reading Data from Server Failed
48188  * @const 
48189  */
48190 Roo.form.Action.LOAD_FAILURE = 'load';
48191
48192 Roo.form.Action.prototype = {
48193     type : 'default',
48194     failureType : undefined,
48195     response : undefined,
48196     result : undefined,
48197
48198     // interface method
48199     run : function(options){
48200
48201     },
48202
48203     // interface method
48204     success : function(response){
48205
48206     },
48207
48208     // interface method
48209     handleResponse : function(response){
48210
48211     },
48212
48213     // default connection failure
48214     failure : function(response){
48215         
48216         this.response = response;
48217         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48218         this.form.afterAction(this, false);
48219     },
48220
48221     processResponse : function(response){
48222         this.response = response;
48223         if(!response.responseText){
48224             return true;
48225         }
48226         this.result = this.handleResponse(response);
48227         return this.result;
48228     },
48229
48230     // utility functions used internally
48231     getUrl : function(appendParams){
48232         var url = this.options.url || this.form.url || this.form.el.dom.action;
48233         if(appendParams){
48234             var p = this.getParams();
48235             if(p){
48236                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48237             }
48238         }
48239         return url;
48240     },
48241
48242     getMethod : function(){
48243         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48244     },
48245
48246     getParams : function(){
48247         var bp = this.form.baseParams;
48248         var p = this.options.params;
48249         if(p){
48250             if(typeof p == "object"){
48251                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48252             }else if(typeof p == 'string' && bp){
48253                 p += '&' + Roo.urlEncode(bp);
48254             }
48255         }else if(bp){
48256             p = Roo.urlEncode(bp);
48257         }
48258         return p;
48259     },
48260
48261     createCallback : function(){
48262         return {
48263             success: this.success,
48264             failure: this.failure,
48265             scope: this,
48266             timeout: (this.form.timeout*1000),
48267             upload: this.form.fileUpload ? this.success : undefined
48268         };
48269     }
48270 };
48271
48272 Roo.form.Action.Submit = function(form, options){
48273     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48274 };
48275
48276 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48277     type : 'submit',
48278
48279     haveProgress : false,
48280     uploadComplete : false,
48281     
48282     // uploadProgress indicator.
48283     uploadProgress : function()
48284     {
48285         if (!this.form.progressUrl) {
48286             return;
48287         }
48288         
48289         if (!this.haveProgress) {
48290             Roo.MessageBox.progress("Uploading", "Uploading");
48291         }
48292         if (this.uploadComplete) {
48293            Roo.MessageBox.hide();
48294            return;
48295         }
48296         
48297         this.haveProgress = true;
48298    
48299         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48300         
48301         var c = new Roo.data.Connection();
48302         c.request({
48303             url : this.form.progressUrl,
48304             params: {
48305                 id : uid
48306             },
48307             method: 'GET',
48308             success : function(req){
48309                //console.log(data);
48310                 var rdata = false;
48311                 var edata;
48312                 try  {
48313                    rdata = Roo.decode(req.responseText)
48314                 } catch (e) {
48315                     Roo.log("Invalid data from server..");
48316                     Roo.log(edata);
48317                     return;
48318                 }
48319                 if (!rdata || !rdata.success) {
48320                     Roo.log(rdata);
48321                     Roo.MessageBox.alert(Roo.encode(rdata));
48322                     return;
48323                 }
48324                 var data = rdata.data;
48325                 
48326                 if (this.uploadComplete) {
48327                    Roo.MessageBox.hide();
48328                    return;
48329                 }
48330                    
48331                 if (data){
48332                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48333                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48334                     );
48335                 }
48336                 this.uploadProgress.defer(2000,this);
48337             },
48338        
48339             failure: function(data) {
48340                 Roo.log('progress url failed ');
48341                 Roo.log(data);
48342             },
48343             scope : this
48344         });
48345            
48346     },
48347     
48348     
48349     run : function()
48350     {
48351         // run get Values on the form, so it syncs any secondary forms.
48352         this.form.getValues();
48353         
48354         var o = this.options;
48355         var method = this.getMethod();
48356         var isPost = method == 'POST';
48357         if(o.clientValidation === false || this.form.isValid()){
48358             
48359             if (this.form.progressUrl) {
48360                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48361                     (new Date() * 1) + '' + Math.random());
48362                     
48363             } 
48364             
48365             
48366             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48367                 form:this.form.el.dom,
48368                 url:this.getUrl(!isPost),
48369                 method: method,
48370                 params:isPost ? this.getParams() : null,
48371                 isUpload: this.form.fileUpload,
48372                 formData : this.form.formData
48373             }));
48374             
48375             this.uploadProgress();
48376
48377         }else if (o.clientValidation !== false){ // client validation failed
48378             this.failureType = Roo.form.Action.CLIENT_INVALID;
48379             this.form.afterAction(this, false);
48380         }
48381     },
48382
48383     success : function(response)
48384     {
48385         this.uploadComplete= true;
48386         if (this.haveProgress) {
48387             Roo.MessageBox.hide();
48388         }
48389         
48390         
48391         var result = this.processResponse(response);
48392         if(result === true || result.success){
48393             this.form.afterAction(this, true);
48394             return;
48395         }
48396         if(result.errors){
48397             this.form.markInvalid(result.errors);
48398             this.failureType = Roo.form.Action.SERVER_INVALID;
48399         }
48400         this.form.afterAction(this, false);
48401     },
48402     failure : function(response)
48403     {
48404         this.uploadComplete= true;
48405         if (this.haveProgress) {
48406             Roo.MessageBox.hide();
48407         }
48408         
48409         this.response = response;
48410         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48411         this.form.afterAction(this, false);
48412     },
48413     
48414     handleResponse : function(response){
48415         if(this.form.errorReader){
48416             var rs = this.form.errorReader.read(response);
48417             var errors = [];
48418             if(rs.records){
48419                 for(var i = 0, len = rs.records.length; i < len; i++) {
48420                     var r = rs.records[i];
48421                     errors[i] = r.data;
48422                 }
48423             }
48424             if(errors.length < 1){
48425                 errors = null;
48426             }
48427             return {
48428                 success : rs.success,
48429                 errors : errors
48430             };
48431         }
48432         var ret = false;
48433         try {
48434             ret = Roo.decode(response.responseText);
48435         } catch (e) {
48436             ret = {
48437                 success: false,
48438                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48439                 errors : []
48440             };
48441         }
48442         return ret;
48443         
48444     }
48445 });
48446
48447
48448 Roo.form.Action.Load = function(form, options){
48449     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48450     this.reader = this.form.reader;
48451 };
48452
48453 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48454     type : 'load',
48455
48456     run : function(){
48457         
48458         Roo.Ajax.request(Roo.apply(
48459                 this.createCallback(), {
48460                     method:this.getMethod(),
48461                     url:this.getUrl(false),
48462                     params:this.getParams()
48463         }));
48464     },
48465
48466     success : function(response){
48467         
48468         var result = this.processResponse(response);
48469         if(result === true || !result.success || !result.data){
48470             this.failureType = Roo.form.Action.LOAD_FAILURE;
48471             this.form.afterAction(this, false);
48472             return;
48473         }
48474         this.form.clearInvalid();
48475         this.form.setValues(result.data);
48476         this.form.afterAction(this, true);
48477     },
48478
48479     handleResponse : function(response){
48480         if(this.form.reader){
48481             var rs = this.form.reader.read(response);
48482             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48483             return {
48484                 success : rs.success,
48485                 data : data
48486             };
48487         }
48488         return Roo.decode(response.responseText);
48489     }
48490 });
48491
48492 Roo.form.Action.ACTION_TYPES = {
48493     'load' : Roo.form.Action.Load,
48494     'submit' : Roo.form.Action.Submit
48495 };/*
48496  * Based on:
48497  * Ext JS Library 1.1.1
48498  * Copyright(c) 2006-2007, Ext JS, LLC.
48499  *
48500  * Originally Released Under LGPL - original licence link has changed is not relivant.
48501  *
48502  * Fork - LGPL
48503  * <script type="text/javascript">
48504  */
48505  
48506 /**
48507  * @class Roo.form.Layout
48508  * @extends Roo.Component
48509  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48510  * @constructor
48511  * @param {Object} config Configuration options
48512  */
48513 Roo.form.Layout = function(config){
48514     var xitems = [];
48515     if (config.items) {
48516         xitems = config.items;
48517         delete config.items;
48518     }
48519     Roo.form.Layout.superclass.constructor.call(this, config);
48520     this.stack = [];
48521     Roo.each(xitems, this.addxtype, this);
48522      
48523 };
48524
48525 Roo.extend(Roo.form.Layout, Roo.Component, {
48526     /**
48527      * @cfg {String/Object} autoCreate
48528      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48529      */
48530     /**
48531      * @cfg {String/Object/Function} style
48532      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48533      * a function which returns such a specification.
48534      */
48535     /**
48536      * @cfg {String} labelAlign
48537      * Valid values are "left," "top" and "right" (defaults to "left")
48538      */
48539     /**
48540      * @cfg {Number} labelWidth
48541      * Fixed width in pixels of all field labels (defaults to undefined)
48542      */
48543     /**
48544      * @cfg {Boolean} clear
48545      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48546      */
48547     clear : true,
48548     /**
48549      * @cfg {String} labelSeparator
48550      * The separator to use after field labels (defaults to ':')
48551      */
48552     labelSeparator : ':',
48553     /**
48554      * @cfg {Boolean} hideLabels
48555      * True to suppress the display of field labels in this layout (defaults to false)
48556      */
48557     hideLabels : false,
48558
48559     // private
48560     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48561     
48562     isLayout : true,
48563     
48564     // private
48565     onRender : function(ct, position){
48566         if(this.el){ // from markup
48567             this.el = Roo.get(this.el);
48568         }else {  // generate
48569             var cfg = this.getAutoCreate();
48570             this.el = ct.createChild(cfg, position);
48571         }
48572         if(this.style){
48573             this.el.applyStyles(this.style);
48574         }
48575         if(this.labelAlign){
48576             this.el.addClass('x-form-label-'+this.labelAlign);
48577         }
48578         if(this.hideLabels){
48579             this.labelStyle = "display:none";
48580             this.elementStyle = "padding-left:0;";
48581         }else{
48582             if(typeof this.labelWidth == 'number'){
48583                 this.labelStyle = "width:"+this.labelWidth+"px;";
48584                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48585             }
48586             if(this.labelAlign == 'top'){
48587                 this.labelStyle = "width:auto;";
48588                 this.elementStyle = "padding-left:0;";
48589             }
48590         }
48591         var stack = this.stack;
48592         var slen = stack.length;
48593         if(slen > 0){
48594             if(!this.fieldTpl){
48595                 var t = new Roo.Template(
48596                     '<div class="x-form-item {5}">',
48597                         '<label for="{0}" style="{2}">{1}{4}</label>',
48598                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48599                         '</div>',
48600                     '</div><div class="x-form-clear-left"></div>'
48601                 );
48602                 t.disableFormats = true;
48603                 t.compile();
48604                 Roo.form.Layout.prototype.fieldTpl = t;
48605             }
48606             for(var i = 0; i < slen; i++) {
48607                 if(stack[i].isFormField){
48608                     this.renderField(stack[i]);
48609                 }else{
48610                     this.renderComponent(stack[i]);
48611                 }
48612             }
48613         }
48614         if(this.clear){
48615             this.el.createChild({cls:'x-form-clear'});
48616         }
48617     },
48618
48619     // private
48620     renderField : function(f){
48621         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48622                f.id, //0
48623                f.fieldLabel, //1
48624                f.labelStyle||this.labelStyle||'', //2
48625                this.elementStyle||'', //3
48626                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48627                f.itemCls||this.itemCls||''  //5
48628        ], true).getPrevSibling());
48629     },
48630
48631     // private
48632     renderComponent : function(c){
48633         c.render(c.isLayout ? this.el : this.el.createChild());    
48634     },
48635     /**
48636      * Adds a object form elements (using the xtype property as the factory method.)
48637      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48638      * @param {Object} config 
48639      */
48640     addxtype : function(o)
48641     {
48642         // create the lement.
48643         o.form = this.form;
48644         var fe = Roo.factory(o, Roo.form);
48645         this.form.allItems.push(fe);
48646         this.stack.push(fe);
48647         
48648         if (fe.isFormField) {
48649             this.form.items.add(fe);
48650         }
48651          
48652         return fe;
48653     }
48654 });
48655
48656 /**
48657  * @class Roo.form.Column
48658  * @extends Roo.form.Layout
48659  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48660  * @constructor
48661  * @param {Object} config Configuration options
48662  */
48663 Roo.form.Column = function(config){
48664     Roo.form.Column.superclass.constructor.call(this, config);
48665 };
48666
48667 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48668     /**
48669      * @cfg {Number/String} width
48670      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48671      */
48672     /**
48673      * @cfg {String/Object} autoCreate
48674      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48675      */
48676
48677     // private
48678     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48679
48680     // private
48681     onRender : function(ct, position){
48682         Roo.form.Column.superclass.onRender.call(this, ct, position);
48683         if(this.width){
48684             this.el.setWidth(this.width);
48685         }
48686     }
48687 });
48688
48689
48690 /**
48691  * @class Roo.form.Row
48692  * @extends Roo.form.Layout
48693  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48694  * @constructor
48695  * @param {Object} config Configuration options
48696  */
48697
48698  
48699 Roo.form.Row = function(config){
48700     Roo.form.Row.superclass.constructor.call(this, config);
48701 };
48702  
48703 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48704       /**
48705      * @cfg {Number/String} width
48706      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48707      */
48708     /**
48709      * @cfg {Number/String} height
48710      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48711      */
48712     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48713     
48714     padWidth : 20,
48715     // private
48716     onRender : function(ct, position){
48717         //console.log('row render');
48718         if(!this.rowTpl){
48719             var t = new Roo.Template(
48720                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48721                     '<label for="{0}" style="{2}">{1}{4}</label>',
48722                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48723                     '</div>',
48724                 '</div>'
48725             );
48726             t.disableFormats = true;
48727             t.compile();
48728             Roo.form.Layout.prototype.rowTpl = t;
48729         }
48730         this.fieldTpl = this.rowTpl;
48731         
48732         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48733         var labelWidth = 100;
48734         
48735         if ((this.labelAlign != 'top')) {
48736             if (typeof this.labelWidth == 'number') {
48737                 labelWidth = this.labelWidth
48738             }
48739             this.padWidth =  20 + labelWidth;
48740             
48741         }
48742         
48743         Roo.form.Column.superclass.onRender.call(this, ct, position);
48744         if(this.width){
48745             this.el.setWidth(this.width);
48746         }
48747         if(this.height){
48748             this.el.setHeight(this.height);
48749         }
48750     },
48751     
48752     // private
48753     renderField : function(f){
48754         f.fieldEl = this.fieldTpl.append(this.el, [
48755                f.id, f.fieldLabel,
48756                f.labelStyle||this.labelStyle||'',
48757                this.elementStyle||'',
48758                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48759                f.itemCls||this.itemCls||'',
48760                f.width ? f.width + this.padWidth : 160 + this.padWidth
48761        ],true);
48762     }
48763 });
48764  
48765
48766 /**
48767  * @class Roo.form.FieldSet
48768  * @extends Roo.form.Layout
48769  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48770  * @constructor
48771  * @param {Object} config Configuration options
48772  */
48773 Roo.form.FieldSet = function(config){
48774     Roo.form.FieldSet.superclass.constructor.call(this, config);
48775 };
48776
48777 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48778     /**
48779      * @cfg {String} legend
48780      * The text to display as the legend for the FieldSet (defaults to '')
48781      */
48782     /**
48783      * @cfg {String/Object} autoCreate
48784      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48785      */
48786
48787     // private
48788     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48789
48790     // private
48791     onRender : function(ct, position){
48792         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48793         if(this.legend){
48794             this.setLegend(this.legend);
48795         }
48796     },
48797
48798     // private
48799     setLegend : function(text){
48800         if(this.rendered){
48801             this.el.child('legend').update(text);
48802         }
48803     }
48804 });/*
48805  * Based on:
48806  * Ext JS Library 1.1.1
48807  * Copyright(c) 2006-2007, Ext JS, LLC.
48808  *
48809  * Originally Released Under LGPL - original licence link has changed is not relivant.
48810  *
48811  * Fork - LGPL
48812  * <script type="text/javascript">
48813  */
48814 /**
48815  * @class Roo.form.VTypes
48816  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48817  * @singleton
48818  */
48819 Roo.form.VTypes = function(){
48820     // closure these in so they are only created once.
48821     var alpha = /^[a-zA-Z_]+$/;
48822     var alphanum = /^[a-zA-Z0-9_]+$/;
48823     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48824     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48825
48826     // All these messages and functions are configurable
48827     return {
48828         /**
48829          * The function used to validate email addresses
48830          * @param {String} value The email address
48831          */
48832         'email' : function(v){
48833             return email.test(v);
48834         },
48835         /**
48836          * The error text to display when the email validation function returns false
48837          * @type String
48838          */
48839         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48840         /**
48841          * The keystroke filter mask to be applied on email input
48842          * @type RegExp
48843          */
48844         'emailMask' : /[a-z0-9_\.\-@]/i,
48845
48846         /**
48847          * The function used to validate URLs
48848          * @param {String} value The URL
48849          */
48850         'url' : function(v){
48851             return url.test(v);
48852         },
48853         /**
48854          * The error text to display when the url validation function returns false
48855          * @type String
48856          */
48857         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48858         
48859         /**
48860          * The function used to validate alpha values
48861          * @param {String} value The value
48862          */
48863         'alpha' : function(v){
48864             return alpha.test(v);
48865         },
48866         /**
48867          * The error text to display when the alpha validation function returns false
48868          * @type String
48869          */
48870         'alphaText' : 'This field should only contain letters and _',
48871         /**
48872          * The keystroke filter mask to be applied on alpha input
48873          * @type RegExp
48874          */
48875         'alphaMask' : /[a-z_]/i,
48876
48877         /**
48878          * The function used to validate alphanumeric values
48879          * @param {String} value The value
48880          */
48881         'alphanum' : function(v){
48882             return alphanum.test(v);
48883         },
48884         /**
48885          * The error text to display when the alphanumeric validation function returns false
48886          * @type String
48887          */
48888         'alphanumText' : 'This field should only contain letters, numbers and _',
48889         /**
48890          * The keystroke filter mask to be applied on alphanumeric input
48891          * @type RegExp
48892          */
48893         'alphanumMask' : /[a-z0-9_]/i
48894     };
48895 }();//<script type="text/javascript">
48896
48897 /**
48898  * @class Roo.form.FCKeditor
48899  * @extends Roo.form.TextArea
48900  * Wrapper around the FCKEditor http://www.fckeditor.net
48901  * @constructor
48902  * Creates a new FCKeditor
48903  * @param {Object} config Configuration options
48904  */
48905 Roo.form.FCKeditor = function(config){
48906     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48907     this.addEvents({
48908          /**
48909          * @event editorinit
48910          * Fired when the editor is initialized - you can add extra handlers here..
48911          * @param {FCKeditor} this
48912          * @param {Object} the FCK object.
48913          */
48914         editorinit : true
48915     });
48916     
48917     
48918 };
48919 Roo.form.FCKeditor.editors = { };
48920 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48921 {
48922     //defaultAutoCreate : {
48923     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48924     //},
48925     // private
48926     /**
48927      * @cfg {Object} fck options - see fck manual for details.
48928      */
48929     fckconfig : false,
48930     
48931     /**
48932      * @cfg {Object} fck toolbar set (Basic or Default)
48933      */
48934     toolbarSet : 'Basic',
48935     /**
48936      * @cfg {Object} fck BasePath
48937      */ 
48938     basePath : '/fckeditor/',
48939     
48940     
48941     frame : false,
48942     
48943     value : '',
48944     
48945    
48946     onRender : function(ct, position)
48947     {
48948         if(!this.el){
48949             this.defaultAutoCreate = {
48950                 tag: "textarea",
48951                 style:"width:300px;height:60px;",
48952                 autocomplete: "new-password"
48953             };
48954         }
48955         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48956         /*
48957         if(this.grow){
48958             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48959             if(this.preventScrollbars){
48960                 this.el.setStyle("overflow", "hidden");
48961             }
48962             this.el.setHeight(this.growMin);
48963         }
48964         */
48965         //console.log('onrender' + this.getId() );
48966         Roo.form.FCKeditor.editors[this.getId()] = this;
48967          
48968
48969         this.replaceTextarea() ;
48970         
48971     },
48972     
48973     getEditor : function() {
48974         return this.fckEditor;
48975     },
48976     /**
48977      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48978      * @param {Mixed} value The value to set
48979      */
48980     
48981     
48982     setValue : function(value)
48983     {
48984         //console.log('setValue: ' + value);
48985         
48986         if(typeof(value) == 'undefined') { // not sure why this is happending...
48987             return;
48988         }
48989         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48990         
48991         //if(!this.el || !this.getEditor()) {
48992         //    this.value = value;
48993             //this.setValue.defer(100,this,[value]);    
48994         //    return;
48995         //} 
48996         
48997         if(!this.getEditor()) {
48998             return;
48999         }
49000         
49001         this.getEditor().SetData(value);
49002         
49003         //
49004
49005     },
49006
49007     /**
49008      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49009      * @return {Mixed} value The field value
49010      */
49011     getValue : function()
49012     {
49013         
49014         if (this.frame && this.frame.dom.style.display == 'none') {
49015             return Roo.form.FCKeditor.superclass.getValue.call(this);
49016         }
49017         
49018         if(!this.el || !this.getEditor()) {
49019            
49020            // this.getValue.defer(100,this); 
49021             return this.value;
49022         }
49023        
49024         
49025         var value=this.getEditor().GetData();
49026         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49027         return Roo.form.FCKeditor.superclass.getValue.call(this);
49028         
49029
49030     },
49031
49032     /**
49033      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49034      * @return {Mixed} value The field value
49035      */
49036     getRawValue : function()
49037     {
49038         if (this.frame && this.frame.dom.style.display == 'none') {
49039             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49040         }
49041         
49042         if(!this.el || !this.getEditor()) {
49043             //this.getRawValue.defer(100,this); 
49044             return this.value;
49045             return;
49046         }
49047         
49048         
49049         
49050         var value=this.getEditor().GetData();
49051         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49052         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49053          
49054     },
49055     
49056     setSize : function(w,h) {
49057         
49058         
49059         
49060         //if (this.frame && this.frame.dom.style.display == 'none') {
49061         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49062         //    return;
49063         //}
49064         //if(!this.el || !this.getEditor()) {
49065         //    this.setSize.defer(100,this, [w,h]); 
49066         //    return;
49067         //}
49068         
49069         
49070         
49071         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49072         
49073         this.frame.dom.setAttribute('width', w);
49074         this.frame.dom.setAttribute('height', h);
49075         this.frame.setSize(w,h);
49076         
49077     },
49078     
49079     toggleSourceEdit : function(value) {
49080         
49081       
49082          
49083         this.el.dom.style.display = value ? '' : 'none';
49084         this.frame.dom.style.display = value ?  'none' : '';
49085         
49086     },
49087     
49088     
49089     focus: function(tag)
49090     {
49091         if (this.frame.dom.style.display == 'none') {
49092             return Roo.form.FCKeditor.superclass.focus.call(this);
49093         }
49094         if(!this.el || !this.getEditor()) {
49095             this.focus.defer(100,this, [tag]); 
49096             return;
49097         }
49098         
49099         
49100         
49101         
49102         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49103         this.getEditor().Focus();
49104         if (tgs.length) {
49105             if (!this.getEditor().Selection.GetSelection()) {
49106                 this.focus.defer(100,this, [tag]); 
49107                 return;
49108             }
49109             
49110             
49111             var r = this.getEditor().EditorDocument.createRange();
49112             r.setStart(tgs[0],0);
49113             r.setEnd(tgs[0],0);
49114             this.getEditor().Selection.GetSelection().removeAllRanges();
49115             this.getEditor().Selection.GetSelection().addRange(r);
49116             this.getEditor().Focus();
49117         }
49118         
49119     },
49120     
49121     
49122     
49123     replaceTextarea : function()
49124     {
49125         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49126             return ;
49127         }
49128         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49129         //{
49130             // We must check the elements firstly using the Id and then the name.
49131         var oTextarea = document.getElementById( this.getId() );
49132         
49133         var colElementsByName = document.getElementsByName( this.getId() ) ;
49134          
49135         oTextarea.style.display = 'none' ;
49136
49137         if ( oTextarea.tabIndex ) {            
49138             this.TabIndex = oTextarea.tabIndex ;
49139         }
49140         
49141         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49142         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49143         this.frame = Roo.get(this.getId() + '___Frame')
49144     },
49145     
49146     _getConfigHtml : function()
49147     {
49148         var sConfig = '' ;
49149
49150         for ( var o in this.fckconfig ) {
49151             sConfig += sConfig.length > 0  ? '&amp;' : '';
49152             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49153         }
49154
49155         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49156     },
49157     
49158     
49159     _getIFrameHtml : function()
49160     {
49161         var sFile = 'fckeditor.html' ;
49162         /* no idea what this is about..
49163         try
49164         {
49165             if ( (/fcksource=true/i).test( window.top.location.search ) )
49166                 sFile = 'fckeditor.original.html' ;
49167         }
49168         catch (e) { 
49169         */
49170
49171         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49172         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49173         
49174         
49175         var html = '<iframe id="' + this.getId() +
49176             '___Frame" src="' + sLink +
49177             '" width="' + this.width +
49178             '" height="' + this.height + '"' +
49179             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49180             ' frameborder="0" scrolling="no"></iframe>' ;
49181
49182         return html ;
49183     },
49184     
49185     _insertHtmlBefore : function( html, element )
49186     {
49187         if ( element.insertAdjacentHTML )       {
49188             // IE
49189             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49190         } else { // Gecko
49191             var oRange = document.createRange() ;
49192             oRange.setStartBefore( element ) ;
49193             var oFragment = oRange.createContextualFragment( html );
49194             element.parentNode.insertBefore( oFragment, element ) ;
49195         }
49196     }
49197     
49198     
49199   
49200     
49201     
49202     
49203     
49204
49205 });
49206
49207 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49208
49209 function FCKeditor_OnComplete(editorInstance){
49210     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49211     f.fckEditor = editorInstance;
49212     //console.log("loaded");
49213     f.fireEvent('editorinit', f, editorInstance);
49214
49215   
49216
49217  
49218
49219
49220
49221
49222
49223
49224
49225
49226
49227
49228
49229
49230
49231
49232
49233 //<script type="text/javascript">
49234 /**
49235  * @class Roo.form.GridField
49236  * @extends Roo.form.Field
49237  * Embed a grid (or editable grid into a form)
49238  * STATUS ALPHA
49239  * 
49240  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49241  * it needs 
49242  * xgrid.store = Roo.data.Store
49243  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49244  * xgrid.store.reader = Roo.data.JsonReader 
49245  * 
49246  * 
49247  * @constructor
49248  * Creates a new GridField
49249  * @param {Object} config Configuration options
49250  */
49251 Roo.form.GridField = function(config){
49252     Roo.form.GridField.superclass.constructor.call(this, config);
49253      
49254 };
49255
49256 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49257     /**
49258      * @cfg {Number} width  - used to restrict width of grid..
49259      */
49260     width : 100,
49261     /**
49262      * @cfg {Number} height - used to restrict height of grid..
49263      */
49264     height : 50,
49265      /**
49266      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49267          * 
49268          *}
49269      */
49270     xgrid : false, 
49271     /**
49272      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49273      * {tag: "input", type: "checkbox", autocomplete: "off"})
49274      */
49275    // defaultAutoCreate : { tag: 'div' },
49276     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49277     /**
49278      * @cfg {String} addTitle Text to include for adding a title.
49279      */
49280     addTitle : false,
49281     //
49282     onResize : function(){
49283         Roo.form.Field.superclass.onResize.apply(this, arguments);
49284     },
49285
49286     initEvents : function(){
49287         // Roo.form.Checkbox.superclass.initEvents.call(this);
49288         // has no events...
49289        
49290     },
49291
49292
49293     getResizeEl : function(){
49294         return this.wrap;
49295     },
49296
49297     getPositionEl : function(){
49298         return this.wrap;
49299     },
49300
49301     // private
49302     onRender : function(ct, position){
49303         
49304         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49305         var style = this.style;
49306         delete this.style;
49307         
49308         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49309         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49310         this.viewEl = this.wrap.createChild({ tag: 'div' });
49311         if (style) {
49312             this.viewEl.applyStyles(style);
49313         }
49314         if (this.width) {
49315             this.viewEl.setWidth(this.width);
49316         }
49317         if (this.height) {
49318             this.viewEl.setHeight(this.height);
49319         }
49320         //if(this.inputValue !== undefined){
49321         //this.setValue(this.value);
49322         
49323         
49324         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49325         
49326         
49327         this.grid.render();
49328         this.grid.getDataSource().on('remove', this.refreshValue, this);
49329         this.grid.getDataSource().on('update', this.refreshValue, this);
49330         this.grid.on('afteredit', this.refreshValue, this);
49331  
49332     },
49333      
49334     
49335     /**
49336      * Sets the value of the item. 
49337      * @param {String} either an object  or a string..
49338      */
49339     setValue : function(v){
49340         //this.value = v;
49341         v = v || []; // empty set..
49342         // this does not seem smart - it really only affects memoryproxy grids..
49343         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49344             var ds = this.grid.getDataSource();
49345             // assumes a json reader..
49346             var data = {}
49347             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49348             ds.loadData( data);
49349         }
49350         // clear selection so it does not get stale.
49351         if (this.grid.sm) { 
49352             this.grid.sm.clearSelections();
49353         }
49354         
49355         Roo.form.GridField.superclass.setValue.call(this, v);
49356         this.refreshValue();
49357         // should load data in the grid really....
49358     },
49359     
49360     // private
49361     refreshValue: function() {
49362          var val = [];
49363         this.grid.getDataSource().each(function(r) {
49364             val.push(r.data);
49365         });
49366         this.el.dom.value = Roo.encode(val);
49367     }
49368     
49369      
49370     
49371     
49372 });/*
49373  * Based on:
49374  * Ext JS Library 1.1.1
49375  * Copyright(c) 2006-2007, Ext JS, LLC.
49376  *
49377  * Originally Released Under LGPL - original licence link has changed is not relivant.
49378  *
49379  * Fork - LGPL
49380  * <script type="text/javascript">
49381  */
49382 /**
49383  * @class Roo.form.DisplayField
49384  * @extends Roo.form.Field
49385  * A generic Field to display non-editable data.
49386  * @cfg {Boolean} closable (true|false) default false
49387  * @constructor
49388  * Creates a new Display Field item.
49389  * @param {Object} config Configuration options
49390  */
49391 Roo.form.DisplayField = function(config){
49392     Roo.form.DisplayField.superclass.constructor.call(this, config);
49393     
49394     this.addEvents({
49395         /**
49396          * @event close
49397          * Fires after the click the close btn
49398              * @param {Roo.form.DisplayField} this
49399              */
49400         close : true
49401     });
49402 };
49403
49404 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49405     inputType:      'hidden',
49406     allowBlank:     true,
49407     readOnly:         true,
49408     
49409  
49410     /**
49411      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49412      */
49413     focusClass : undefined,
49414     /**
49415      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49416      */
49417     fieldClass: 'x-form-field',
49418     
49419      /**
49420      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49421      */
49422     valueRenderer: undefined,
49423     
49424     width: 100,
49425     /**
49426      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49427      * {tag: "input", type: "checkbox", autocomplete: "off"})
49428      */
49429      
49430  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49431  
49432     closable : false,
49433     
49434     onResize : function(){
49435         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49436         
49437     },
49438
49439     initEvents : function(){
49440         // Roo.form.Checkbox.superclass.initEvents.call(this);
49441         // has no events...
49442         
49443         if(this.closable){
49444             this.closeEl.on('click', this.onClose, this);
49445         }
49446        
49447     },
49448
49449
49450     getResizeEl : function(){
49451         return this.wrap;
49452     },
49453
49454     getPositionEl : function(){
49455         return this.wrap;
49456     },
49457
49458     // private
49459     onRender : function(ct, position){
49460         
49461         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49462         //if(this.inputValue !== undefined){
49463         this.wrap = this.el.wrap();
49464         
49465         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49466         
49467         if(this.closable){
49468             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49469         }
49470         
49471         if (this.bodyStyle) {
49472             this.viewEl.applyStyles(this.bodyStyle);
49473         }
49474         //this.viewEl.setStyle('padding', '2px');
49475         
49476         this.setValue(this.value);
49477         
49478     },
49479 /*
49480     // private
49481     initValue : Roo.emptyFn,
49482
49483   */
49484
49485         // private
49486     onClick : function(){
49487         
49488     },
49489
49490     /**
49491      * Sets the checked state of the checkbox.
49492      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49493      */
49494     setValue : function(v){
49495         this.value = v;
49496         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49497         // this might be called before we have a dom element..
49498         if (!this.viewEl) {
49499             return;
49500         }
49501         this.viewEl.dom.innerHTML = html;
49502         Roo.form.DisplayField.superclass.setValue.call(this, v);
49503
49504     },
49505     
49506     onClose : function(e)
49507     {
49508         e.preventDefault();
49509         
49510         this.fireEvent('close', this);
49511     }
49512 });/*
49513  * 
49514  * Licence- LGPL
49515  * 
49516  */
49517
49518 /**
49519  * @class Roo.form.DayPicker
49520  * @extends Roo.form.Field
49521  * A Day picker show [M] [T] [W] ....
49522  * @constructor
49523  * Creates a new Day Picker
49524  * @param {Object} config Configuration options
49525  */
49526 Roo.form.DayPicker= function(config){
49527     Roo.form.DayPicker.superclass.constructor.call(this, config);
49528      
49529 };
49530
49531 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49532     /**
49533      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49534      */
49535     focusClass : undefined,
49536     /**
49537      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49538      */
49539     fieldClass: "x-form-field",
49540    
49541     /**
49542      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49543      * {tag: "input", type: "checkbox", autocomplete: "off"})
49544      */
49545     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49546     
49547    
49548     actionMode : 'viewEl', 
49549     //
49550     // private
49551  
49552     inputType : 'hidden',
49553     
49554      
49555     inputElement: false, // real input element?
49556     basedOn: false, // ????
49557     
49558     isFormField: true, // not sure where this is needed!!!!
49559
49560     onResize : function(){
49561         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49562         if(!this.boxLabel){
49563             this.el.alignTo(this.wrap, 'c-c');
49564         }
49565     },
49566
49567     initEvents : function(){
49568         Roo.form.Checkbox.superclass.initEvents.call(this);
49569         this.el.on("click", this.onClick,  this);
49570         this.el.on("change", this.onClick,  this);
49571     },
49572
49573
49574     getResizeEl : function(){
49575         return this.wrap;
49576     },
49577
49578     getPositionEl : function(){
49579         return this.wrap;
49580     },
49581
49582     
49583     // private
49584     onRender : function(ct, position){
49585         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49586        
49587         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49588         
49589         var r1 = '<table><tr>';
49590         var r2 = '<tr class="x-form-daypick-icons">';
49591         for (var i=0; i < 7; i++) {
49592             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49593             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49594         }
49595         
49596         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49597         viewEl.select('img').on('click', this.onClick, this);
49598         this.viewEl = viewEl;   
49599         
49600         
49601         // this will not work on Chrome!!!
49602         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49603         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49604         
49605         
49606           
49607
49608     },
49609
49610     // private
49611     initValue : Roo.emptyFn,
49612
49613     /**
49614      * Returns the checked state of the checkbox.
49615      * @return {Boolean} True if checked, else false
49616      */
49617     getValue : function(){
49618         return this.el.dom.value;
49619         
49620     },
49621
49622         // private
49623     onClick : function(e){ 
49624         //this.setChecked(!this.checked);
49625         Roo.get(e.target).toggleClass('x-menu-item-checked');
49626         this.refreshValue();
49627         //if(this.el.dom.checked != this.checked){
49628         //    this.setValue(this.el.dom.checked);
49629        // }
49630     },
49631     
49632     // private
49633     refreshValue : function()
49634     {
49635         var val = '';
49636         this.viewEl.select('img',true).each(function(e,i,n)  {
49637             val += e.is(".x-menu-item-checked") ? String(n) : '';
49638         });
49639         this.setValue(val, true);
49640     },
49641
49642     /**
49643      * Sets the checked state of the checkbox.
49644      * On is always based on a string comparison between inputValue and the param.
49645      * @param {Boolean/String} value - the value to set 
49646      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49647      */
49648     setValue : function(v,suppressEvent){
49649         if (!this.el.dom) {
49650             return;
49651         }
49652         var old = this.el.dom.value ;
49653         this.el.dom.value = v;
49654         if (suppressEvent) {
49655             return ;
49656         }
49657          
49658         // update display..
49659         this.viewEl.select('img',true).each(function(e,i,n)  {
49660             
49661             var on = e.is(".x-menu-item-checked");
49662             var newv = v.indexOf(String(n)) > -1;
49663             if (on != newv) {
49664                 e.toggleClass('x-menu-item-checked');
49665             }
49666             
49667         });
49668         
49669         
49670         this.fireEvent('change', this, v, old);
49671         
49672         
49673     },
49674    
49675     // handle setting of hidden value by some other method!!?!?
49676     setFromHidden: function()
49677     {
49678         if(!this.el){
49679             return;
49680         }
49681         //console.log("SET FROM HIDDEN");
49682         //alert('setFrom hidden');
49683         this.setValue(this.el.dom.value);
49684     },
49685     
49686     onDestroy : function()
49687     {
49688         if(this.viewEl){
49689             Roo.get(this.viewEl).remove();
49690         }
49691          
49692         Roo.form.DayPicker.superclass.onDestroy.call(this);
49693     }
49694
49695 });/*
49696  * RooJS Library 1.1.1
49697  * Copyright(c) 2008-2011  Alan Knowles
49698  *
49699  * License - LGPL
49700  */
49701  
49702
49703 /**
49704  * @class Roo.form.ComboCheck
49705  * @extends Roo.form.ComboBox
49706  * A combobox for multiple select items.
49707  *
49708  * FIXME - could do with a reset button..
49709  * 
49710  * @constructor
49711  * Create a new ComboCheck
49712  * @param {Object} config Configuration options
49713  */
49714 Roo.form.ComboCheck = function(config){
49715     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49716     // should verify some data...
49717     // like
49718     // hiddenName = required..
49719     // displayField = required
49720     // valudField == required
49721     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49722     var _t = this;
49723     Roo.each(req, function(e) {
49724         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49725             throw "Roo.form.ComboCheck : missing value for: " + e;
49726         }
49727     });
49728     
49729     
49730 };
49731
49732 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49733      
49734      
49735     editable : false,
49736      
49737     selectedClass: 'x-menu-item-checked', 
49738     
49739     // private
49740     onRender : function(ct, position){
49741         var _t = this;
49742         
49743         
49744         
49745         if(!this.tpl){
49746             var cls = 'x-combo-list';
49747
49748             
49749             this.tpl =  new Roo.Template({
49750                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49751                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49752                    '<span>{' + this.displayField + '}</span>' +
49753                     '</div>' 
49754                 
49755             });
49756         }
49757  
49758         
49759         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49760         this.view.singleSelect = false;
49761         this.view.multiSelect = true;
49762         this.view.toggleSelect = true;
49763         this.pageTb.add(new Roo.Toolbar.Fill(), {
49764             
49765             text: 'Done',
49766             handler: function()
49767             {
49768                 _t.collapse();
49769             }
49770         });
49771     },
49772     
49773     onViewOver : function(e, t){
49774         // do nothing...
49775         return;
49776         
49777     },
49778     
49779     onViewClick : function(doFocus,index){
49780         return;
49781         
49782     },
49783     select: function () {
49784         //Roo.log("SELECT CALLED");
49785     },
49786      
49787     selectByValue : function(xv, scrollIntoView){
49788         var ar = this.getValueArray();
49789         var sels = [];
49790         
49791         Roo.each(ar, function(v) {
49792             if(v === undefined || v === null){
49793                 return;
49794             }
49795             var r = this.findRecord(this.valueField, v);
49796             if(r){
49797                 sels.push(this.store.indexOf(r))
49798                 
49799             }
49800         },this);
49801         this.view.select(sels);
49802         return false;
49803     },
49804     
49805     
49806     
49807     onSelect : function(record, index){
49808        // Roo.log("onselect Called");
49809        // this is only called by the clear button now..
49810         this.view.clearSelections();
49811         this.setValue('[]');
49812         if (this.value != this.valueBefore) {
49813             this.fireEvent('change', this, this.value, this.valueBefore);
49814             this.valueBefore = this.value;
49815         }
49816     },
49817     getValueArray : function()
49818     {
49819         var ar = [] ;
49820         
49821         try {
49822             //Roo.log(this.value);
49823             if (typeof(this.value) == 'undefined') {
49824                 return [];
49825             }
49826             var ar = Roo.decode(this.value);
49827             return  ar instanceof Array ? ar : []; //?? valid?
49828             
49829         } catch(e) {
49830             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49831             return [];
49832         }
49833          
49834     },
49835     expand : function ()
49836     {
49837         
49838         Roo.form.ComboCheck.superclass.expand.call(this);
49839         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49840         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49841         
49842
49843     },
49844     
49845     collapse : function(){
49846         Roo.form.ComboCheck.superclass.collapse.call(this);
49847         var sl = this.view.getSelectedIndexes();
49848         var st = this.store;
49849         var nv = [];
49850         var tv = [];
49851         var r;
49852         Roo.each(sl, function(i) {
49853             r = st.getAt(i);
49854             nv.push(r.get(this.valueField));
49855         },this);
49856         this.setValue(Roo.encode(nv));
49857         if (this.value != this.valueBefore) {
49858
49859             this.fireEvent('change', this, this.value, this.valueBefore);
49860             this.valueBefore = this.value;
49861         }
49862         
49863     },
49864     
49865     setValue : function(v){
49866         // Roo.log(v);
49867         this.value = v;
49868         
49869         var vals = this.getValueArray();
49870         var tv = [];
49871         Roo.each(vals, function(k) {
49872             var r = this.findRecord(this.valueField, k);
49873             if(r){
49874                 tv.push(r.data[this.displayField]);
49875             }else if(this.valueNotFoundText !== undefined){
49876                 tv.push( this.valueNotFoundText );
49877             }
49878         },this);
49879        // Roo.log(tv);
49880         
49881         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49882         this.hiddenField.value = v;
49883         this.value = v;
49884     }
49885     
49886 });/*
49887  * Based on:
49888  * Ext JS Library 1.1.1
49889  * Copyright(c) 2006-2007, Ext JS, LLC.
49890  *
49891  * Originally Released Under LGPL - original licence link has changed is not relivant.
49892  *
49893  * Fork - LGPL
49894  * <script type="text/javascript">
49895  */
49896  
49897 /**
49898  * @class Roo.form.Signature
49899  * @extends Roo.form.Field
49900  * Signature field.  
49901  * @constructor
49902  * 
49903  * @param {Object} config Configuration options
49904  */
49905
49906 Roo.form.Signature = function(config){
49907     Roo.form.Signature.superclass.constructor.call(this, config);
49908     
49909     this.addEvents({// not in used??
49910          /**
49911          * @event confirm
49912          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49913              * @param {Roo.form.Signature} combo This combo box
49914              */
49915         'confirm' : true,
49916         /**
49917          * @event reset
49918          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49919              * @param {Roo.form.ComboBox} combo This combo box
49920              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49921              */
49922         'reset' : true
49923     });
49924 };
49925
49926 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49927     /**
49928      * @cfg {Object} labels Label to use when rendering a form.
49929      * defaults to 
49930      * labels : { 
49931      *      clear : "Clear",
49932      *      confirm : "Confirm"
49933      *  }
49934      */
49935     labels : { 
49936         clear : "Clear",
49937         confirm : "Confirm"
49938     },
49939     /**
49940      * @cfg {Number} width The signature panel width (defaults to 300)
49941      */
49942     width: 300,
49943     /**
49944      * @cfg {Number} height The signature panel height (defaults to 100)
49945      */
49946     height : 100,
49947     /**
49948      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49949      */
49950     allowBlank : false,
49951     
49952     //private
49953     // {Object} signPanel The signature SVG panel element (defaults to {})
49954     signPanel : {},
49955     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49956     isMouseDown : false,
49957     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49958     isConfirmed : false,
49959     // {String} signatureTmp SVG mapping string (defaults to empty string)
49960     signatureTmp : '',
49961     
49962     
49963     defaultAutoCreate : { // modified by initCompnoent..
49964         tag: "input",
49965         type:"hidden"
49966     },
49967
49968     // private
49969     onRender : function(ct, position){
49970         
49971         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49972         
49973         this.wrap = this.el.wrap({
49974             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49975         });
49976         
49977         this.createToolbar(this);
49978         this.signPanel = this.wrap.createChild({
49979                 tag: 'div',
49980                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49981             }, this.el
49982         );
49983             
49984         this.svgID = Roo.id();
49985         this.svgEl = this.signPanel.createChild({
49986               xmlns : 'http://www.w3.org/2000/svg',
49987               tag : 'svg',
49988               id : this.svgID + "-svg",
49989               width: this.width,
49990               height: this.height,
49991               viewBox: '0 0 '+this.width+' '+this.height,
49992               cn : [
49993                 {
49994                     tag: "rect",
49995                     id: this.svgID + "-svg-r",
49996                     width: this.width,
49997                     height: this.height,
49998                     fill: "#ffa"
49999                 },
50000                 {
50001                     tag: "line",
50002                     id: this.svgID + "-svg-l",
50003                     x1: "0", // start
50004                     y1: (this.height*0.8), // start set the line in 80% of height
50005                     x2: this.width, // end
50006                     y2: (this.height*0.8), // end set the line in 80% of height
50007                     'stroke': "#666",
50008                     'stroke-width': "1",
50009                     'stroke-dasharray': "3",
50010                     'shape-rendering': "crispEdges",
50011                     'pointer-events': "none"
50012                 },
50013                 {
50014                     tag: "path",
50015                     id: this.svgID + "-svg-p",
50016                     'stroke': "navy",
50017                     'stroke-width': "3",
50018                     'fill': "none",
50019                     'pointer-events': 'none'
50020                 }
50021               ]
50022         });
50023         this.createSVG();
50024         this.svgBox = this.svgEl.dom.getScreenCTM();
50025     },
50026     createSVG : function(){ 
50027         var svg = this.signPanel;
50028         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50029         var t = this;
50030
50031         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50032         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50033         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50034         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50035         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50036         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50037         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50038         
50039     },
50040     isTouchEvent : function(e){
50041         return e.type.match(/^touch/);
50042     },
50043     getCoords : function (e) {
50044         var pt    = this.svgEl.dom.createSVGPoint();
50045         pt.x = e.clientX; 
50046         pt.y = e.clientY;
50047         if (this.isTouchEvent(e)) {
50048             pt.x =  e.targetTouches[0].clientX;
50049             pt.y = e.targetTouches[0].clientY;
50050         }
50051         var a = this.svgEl.dom.getScreenCTM();
50052         var b = a.inverse();
50053         var mx = pt.matrixTransform(b);
50054         return mx.x + ',' + mx.y;
50055     },
50056     //mouse event headler 
50057     down : function (e) {
50058         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50059         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50060         
50061         this.isMouseDown = true;
50062         
50063         e.preventDefault();
50064     },
50065     move : function (e) {
50066         if (this.isMouseDown) {
50067             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50068             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50069         }
50070         
50071         e.preventDefault();
50072     },
50073     up : function (e) {
50074         this.isMouseDown = false;
50075         var sp = this.signatureTmp.split(' ');
50076         
50077         if(sp.length > 1){
50078             if(!sp[sp.length-2].match(/^L/)){
50079                 sp.pop();
50080                 sp.pop();
50081                 sp.push("");
50082                 this.signatureTmp = sp.join(" ");
50083             }
50084         }
50085         if(this.getValue() != this.signatureTmp){
50086             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50087             this.isConfirmed = false;
50088         }
50089         e.preventDefault();
50090     },
50091     
50092     /**
50093      * Protected method that will not generally be called directly. It
50094      * is called when the editor creates its toolbar. Override this method if you need to
50095      * add custom toolbar buttons.
50096      * @param {HtmlEditor} editor
50097      */
50098     createToolbar : function(editor){
50099          function btn(id, toggle, handler){
50100             var xid = fid + '-'+ id ;
50101             return {
50102                 id : xid,
50103                 cmd : id,
50104                 cls : 'x-btn-icon x-edit-'+id,
50105                 enableToggle:toggle !== false,
50106                 scope: editor, // was editor...
50107                 handler:handler||editor.relayBtnCmd,
50108                 clickEvent:'mousedown',
50109                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50110                 tabIndex:-1
50111             };
50112         }
50113         
50114         
50115         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50116         this.tb = tb;
50117         this.tb.add(
50118            {
50119                 cls : ' x-signature-btn x-signature-'+id,
50120                 scope: editor, // was editor...
50121                 handler: this.reset,
50122                 clickEvent:'mousedown',
50123                 text: this.labels.clear
50124             },
50125             {
50126                  xtype : 'Fill',
50127                  xns: Roo.Toolbar
50128             }, 
50129             {
50130                 cls : '  x-signature-btn x-signature-'+id,
50131                 scope: editor, // was editor...
50132                 handler: this.confirmHandler,
50133                 clickEvent:'mousedown',
50134                 text: this.labels.confirm
50135             }
50136         );
50137     
50138     },
50139     //public
50140     /**
50141      * when user is clicked confirm then show this image.....
50142      * 
50143      * @return {String} Image Data URI
50144      */
50145     getImageDataURI : function(){
50146         var svg = this.svgEl.dom.parentNode.innerHTML;
50147         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50148         return src; 
50149     },
50150     /**
50151      * 
50152      * @return {Boolean} this.isConfirmed
50153      */
50154     getConfirmed : function(){
50155         return this.isConfirmed;
50156     },
50157     /**
50158      * 
50159      * @return {Number} this.width
50160      */
50161     getWidth : function(){
50162         return this.width;
50163     },
50164     /**
50165      * 
50166      * @return {Number} this.height
50167      */
50168     getHeight : function(){
50169         return this.height;
50170     },
50171     // private
50172     getSignature : function(){
50173         return this.signatureTmp;
50174     },
50175     // private
50176     reset : function(){
50177         this.signatureTmp = '';
50178         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50179         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50180         this.isConfirmed = false;
50181         Roo.form.Signature.superclass.reset.call(this);
50182     },
50183     setSignature : function(s){
50184         this.signatureTmp = s;
50185         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50186         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50187         this.setValue(s);
50188         this.isConfirmed = false;
50189         Roo.form.Signature.superclass.reset.call(this);
50190     }, 
50191     test : function(){
50192 //        Roo.log(this.signPanel.dom.contentWindow.up())
50193     },
50194     //private
50195     setConfirmed : function(){
50196         
50197         
50198         
50199 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50200     },
50201     // private
50202     confirmHandler : function(){
50203         if(!this.getSignature()){
50204             return;
50205         }
50206         
50207         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50208         this.setValue(this.getSignature());
50209         this.isConfirmed = true;
50210         
50211         this.fireEvent('confirm', this);
50212     },
50213     // private
50214     // Subclasses should provide the validation implementation by overriding this
50215     validateValue : function(value){
50216         if(this.allowBlank){
50217             return true;
50218         }
50219         
50220         if(this.isConfirmed){
50221             return true;
50222         }
50223         return false;
50224     }
50225 });/*
50226  * Based on:
50227  * Ext JS Library 1.1.1
50228  * Copyright(c) 2006-2007, Ext JS, LLC.
50229  *
50230  * Originally Released Under LGPL - original licence link has changed is not relivant.
50231  *
50232  * Fork - LGPL
50233  * <script type="text/javascript">
50234  */
50235  
50236
50237 /**
50238  * @class Roo.form.ComboBox
50239  * @extends Roo.form.TriggerField
50240  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50241  * @constructor
50242  * Create a new ComboBox.
50243  * @param {Object} config Configuration options
50244  */
50245 Roo.form.Select = function(config){
50246     Roo.form.Select.superclass.constructor.call(this, config);
50247      
50248 };
50249
50250 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50251     /**
50252      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50253      */
50254     /**
50255      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50256      * rendering into an Roo.Editor, defaults to false)
50257      */
50258     /**
50259      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50260      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50261      */
50262     /**
50263      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50264      */
50265     /**
50266      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50267      * the dropdown list (defaults to undefined, with no header element)
50268      */
50269
50270      /**
50271      * @cfg {String/Roo.Template} tpl The template to use to render the output
50272      */
50273      
50274     // private
50275     defaultAutoCreate : {tag: "select"  },
50276     /**
50277      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50278      */
50279     listWidth: undefined,
50280     /**
50281      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50282      * mode = 'remote' or 'text' if mode = 'local')
50283      */
50284     displayField: undefined,
50285     /**
50286      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50287      * mode = 'remote' or 'value' if mode = 'local'). 
50288      * Note: use of a valueField requires the user make a selection
50289      * in order for a value to be mapped.
50290      */
50291     valueField: undefined,
50292     
50293     
50294     /**
50295      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50296      * field's data value (defaults to the underlying DOM element's name)
50297      */
50298     hiddenName: undefined,
50299     /**
50300      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50301      */
50302     listClass: '',
50303     /**
50304      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50305      */
50306     selectedClass: 'x-combo-selected',
50307     /**
50308      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50309      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50310      * which displays a downward arrow icon).
50311      */
50312     triggerClass : 'x-form-arrow-trigger',
50313     /**
50314      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50315      */
50316     shadow:'sides',
50317     /**
50318      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50319      * anchor positions (defaults to 'tl-bl')
50320      */
50321     listAlign: 'tl-bl?',
50322     /**
50323      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50324      */
50325     maxHeight: 300,
50326     /**
50327      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50328      * query specified by the allQuery config option (defaults to 'query')
50329      */
50330     triggerAction: 'query',
50331     /**
50332      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50333      * (defaults to 4, does not apply if editable = false)
50334      */
50335     minChars : 4,
50336     /**
50337      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50338      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50339      */
50340     typeAhead: false,
50341     /**
50342      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50343      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50344      */
50345     queryDelay: 500,
50346     /**
50347      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50348      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50349      */
50350     pageSize: 0,
50351     /**
50352      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50353      * when editable = true (defaults to false)
50354      */
50355     selectOnFocus:false,
50356     /**
50357      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50358      */
50359     queryParam: 'query',
50360     /**
50361      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50362      * when mode = 'remote' (defaults to 'Loading...')
50363      */
50364     loadingText: 'Loading...',
50365     /**
50366      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50367      */
50368     resizable: false,
50369     /**
50370      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50371      */
50372     handleHeight : 8,
50373     /**
50374      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50375      * traditional select (defaults to true)
50376      */
50377     editable: true,
50378     /**
50379      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50380      */
50381     allQuery: '',
50382     /**
50383      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50384      */
50385     mode: 'remote',
50386     /**
50387      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50388      * listWidth has a higher value)
50389      */
50390     minListWidth : 70,
50391     /**
50392      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50393      * allow the user to set arbitrary text into the field (defaults to false)
50394      */
50395     forceSelection:false,
50396     /**
50397      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50398      * if typeAhead = true (defaults to 250)
50399      */
50400     typeAheadDelay : 250,
50401     /**
50402      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50403      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50404      */
50405     valueNotFoundText : undefined,
50406     
50407     /**
50408      * @cfg {String} defaultValue The value displayed after loading the store.
50409      */
50410     defaultValue: '',
50411     
50412     /**
50413      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50414      */
50415     blockFocus : false,
50416     
50417     /**
50418      * @cfg {Boolean} disableClear Disable showing of clear button.
50419      */
50420     disableClear : false,
50421     /**
50422      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50423      */
50424     alwaysQuery : false,
50425     
50426     //private
50427     addicon : false,
50428     editicon: false,
50429     
50430     // element that contains real text value.. (when hidden is used..)
50431      
50432     // private
50433     onRender : function(ct, position){
50434         Roo.form.Field.prototype.onRender.call(this, ct, position);
50435         
50436         if(this.store){
50437             this.store.on('beforeload', this.onBeforeLoad, this);
50438             this.store.on('load', this.onLoad, this);
50439             this.store.on('loadexception', this.onLoadException, this);
50440             this.store.load({});
50441         }
50442         
50443         
50444         
50445     },
50446
50447     // private
50448     initEvents : function(){
50449         //Roo.form.ComboBox.superclass.initEvents.call(this);
50450  
50451     },
50452
50453     onDestroy : function(){
50454        
50455         if(this.store){
50456             this.store.un('beforeload', this.onBeforeLoad, this);
50457             this.store.un('load', this.onLoad, this);
50458             this.store.un('loadexception', this.onLoadException, this);
50459         }
50460         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50461     },
50462
50463     // private
50464     fireKey : function(e){
50465         if(e.isNavKeyPress() && !this.list.isVisible()){
50466             this.fireEvent("specialkey", this, e);
50467         }
50468     },
50469
50470     // private
50471     onResize: function(w, h){
50472         
50473         return; 
50474     
50475         
50476     },
50477
50478     /**
50479      * Allow or prevent the user from directly editing the field text.  If false is passed,
50480      * the user will only be able to select from the items defined in the dropdown list.  This method
50481      * is the runtime equivalent of setting the 'editable' config option at config time.
50482      * @param {Boolean} value True to allow the user to directly edit the field text
50483      */
50484     setEditable : function(value){
50485          
50486     },
50487
50488     // private
50489     onBeforeLoad : function(){
50490         
50491         Roo.log("Select before load");
50492         return;
50493     
50494         this.innerList.update(this.loadingText ?
50495                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50496         //this.restrictHeight();
50497         this.selectedIndex = -1;
50498     },
50499
50500     // private
50501     onLoad : function(){
50502
50503     
50504         var dom = this.el.dom;
50505         dom.innerHTML = '';
50506          var od = dom.ownerDocument;
50507          
50508         if (this.emptyText) {
50509             var op = od.createElement('option');
50510             op.setAttribute('value', '');
50511             op.innerHTML = String.format('{0}', this.emptyText);
50512             dom.appendChild(op);
50513         }
50514         if(this.store.getCount() > 0){
50515            
50516             var vf = this.valueField;
50517             var df = this.displayField;
50518             this.store.data.each(function(r) {
50519                 // which colmsn to use... testing - cdoe / title..
50520                 var op = od.createElement('option');
50521                 op.setAttribute('value', r.data[vf]);
50522                 op.innerHTML = String.format('{0}', r.data[df]);
50523                 dom.appendChild(op);
50524             });
50525             if (typeof(this.defaultValue != 'undefined')) {
50526                 this.setValue(this.defaultValue);
50527             }
50528             
50529              
50530         }else{
50531             //this.onEmptyResults();
50532         }
50533         //this.el.focus();
50534     },
50535     // private
50536     onLoadException : function()
50537     {
50538         dom.innerHTML = '';
50539             
50540         Roo.log("Select on load exception");
50541         return;
50542     
50543         this.collapse();
50544         Roo.log(this.store.reader.jsonData);
50545         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50546             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50547         }
50548         
50549         
50550     },
50551     // private
50552     onTypeAhead : function(){
50553          
50554     },
50555
50556     // private
50557     onSelect : function(record, index){
50558         Roo.log('on select?');
50559         return;
50560         if(this.fireEvent('beforeselect', this, record, index) !== false){
50561             this.setFromData(index > -1 ? record.data : false);
50562             this.collapse();
50563             this.fireEvent('select', this, record, index);
50564         }
50565     },
50566
50567     /**
50568      * Returns the currently selected field value or empty string if no value is set.
50569      * @return {String} value The selected value
50570      */
50571     getValue : function(){
50572         var dom = this.el.dom;
50573         this.value = dom.options[dom.selectedIndex].value;
50574         return this.value;
50575         
50576     },
50577
50578     /**
50579      * Clears any text/value currently set in the field
50580      */
50581     clearValue : function(){
50582         this.value = '';
50583         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50584         
50585     },
50586
50587     /**
50588      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50589      * will be displayed in the field.  If the value does not match the data value of an existing item,
50590      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50591      * Otherwise the field will be blank (although the value will still be set).
50592      * @param {String} value The value to match
50593      */
50594     setValue : function(v){
50595         var d = this.el.dom;
50596         for (var i =0; i < d.options.length;i++) {
50597             if (v == d.options[i].value) {
50598                 d.selectedIndex = i;
50599                 this.value = v;
50600                 return;
50601             }
50602         }
50603         this.clearValue();
50604     },
50605     /**
50606      * @property {Object} the last set data for the element
50607      */
50608     
50609     lastData : false,
50610     /**
50611      * Sets the value of the field based on a object which is related to the record format for the store.
50612      * @param {Object} value the value to set as. or false on reset?
50613      */
50614     setFromData : function(o){
50615         Roo.log('setfrom data?');
50616          
50617         
50618         
50619     },
50620     // private
50621     reset : function(){
50622         this.clearValue();
50623     },
50624     // private
50625     findRecord : function(prop, value){
50626         
50627         return false;
50628     
50629         var record;
50630         if(this.store.getCount() > 0){
50631             this.store.each(function(r){
50632                 if(r.data[prop] == value){
50633                     record = r;
50634                     return false;
50635                 }
50636                 return true;
50637             });
50638         }
50639         return record;
50640     },
50641     
50642     getName: function()
50643     {
50644         // returns hidden if it's set..
50645         if (!this.rendered) {return ''};
50646         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50647         
50648     },
50649      
50650
50651     
50652
50653     // private
50654     onEmptyResults : function(){
50655         Roo.log('empty results');
50656         //this.collapse();
50657     },
50658
50659     /**
50660      * Returns true if the dropdown list is expanded, else false.
50661      */
50662     isExpanded : function(){
50663         return false;
50664     },
50665
50666     /**
50667      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50668      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50669      * @param {String} value The data value of the item to select
50670      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50671      * selected item if it is not currently in view (defaults to true)
50672      * @return {Boolean} True if the value matched an item in the list, else false
50673      */
50674     selectByValue : function(v, scrollIntoView){
50675         Roo.log('select By Value');
50676         return false;
50677     
50678         if(v !== undefined && v !== null){
50679             var r = this.findRecord(this.valueField || this.displayField, v);
50680             if(r){
50681                 this.select(this.store.indexOf(r), scrollIntoView);
50682                 return true;
50683             }
50684         }
50685         return false;
50686     },
50687
50688     /**
50689      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50690      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50691      * @param {Number} index The zero-based index of the list item to select
50692      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50693      * selected item if it is not currently in view (defaults to true)
50694      */
50695     select : function(index, scrollIntoView){
50696         Roo.log('select ');
50697         return  ;
50698         
50699         this.selectedIndex = index;
50700         this.view.select(index);
50701         if(scrollIntoView !== false){
50702             var el = this.view.getNode(index);
50703             if(el){
50704                 this.innerList.scrollChildIntoView(el, false);
50705             }
50706         }
50707     },
50708
50709       
50710
50711     // private
50712     validateBlur : function(){
50713         
50714         return;
50715         
50716     },
50717
50718     // private
50719     initQuery : function(){
50720         this.doQuery(this.getRawValue());
50721     },
50722
50723     // private
50724     doForce : function(){
50725         if(this.el.dom.value.length > 0){
50726             this.el.dom.value =
50727                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50728              
50729         }
50730     },
50731
50732     /**
50733      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50734      * query allowing the query action to be canceled if needed.
50735      * @param {String} query The SQL query to execute
50736      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50737      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50738      * saved in the current store (defaults to false)
50739      */
50740     doQuery : function(q, forceAll){
50741         
50742         Roo.log('doQuery?');
50743         if(q === undefined || q === null){
50744             q = '';
50745         }
50746         var qe = {
50747             query: q,
50748             forceAll: forceAll,
50749             combo: this,
50750             cancel:false
50751         };
50752         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50753             return false;
50754         }
50755         q = qe.query;
50756         forceAll = qe.forceAll;
50757         if(forceAll === true || (q.length >= this.minChars)){
50758             if(this.lastQuery != q || this.alwaysQuery){
50759                 this.lastQuery = q;
50760                 if(this.mode == 'local'){
50761                     this.selectedIndex = -1;
50762                     if(forceAll){
50763                         this.store.clearFilter();
50764                     }else{
50765                         this.store.filter(this.displayField, q);
50766                     }
50767                     this.onLoad();
50768                 }else{
50769                     this.store.baseParams[this.queryParam] = q;
50770                     this.store.load({
50771                         params: this.getParams(q)
50772                     });
50773                     this.expand();
50774                 }
50775             }else{
50776                 this.selectedIndex = -1;
50777                 this.onLoad();   
50778             }
50779         }
50780     },
50781
50782     // private
50783     getParams : function(q){
50784         var p = {};
50785         //p[this.queryParam] = q;
50786         if(this.pageSize){
50787             p.start = 0;
50788             p.limit = this.pageSize;
50789         }
50790         return p;
50791     },
50792
50793     /**
50794      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50795      */
50796     collapse : function(){
50797         
50798     },
50799
50800     // private
50801     collapseIf : function(e){
50802         
50803     },
50804
50805     /**
50806      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50807      */
50808     expand : function(){
50809         
50810     } ,
50811
50812     // private
50813      
50814
50815     /** 
50816     * @cfg {Boolean} grow 
50817     * @hide 
50818     */
50819     /** 
50820     * @cfg {Number} growMin 
50821     * @hide 
50822     */
50823     /** 
50824     * @cfg {Number} growMax 
50825     * @hide 
50826     */
50827     /**
50828      * @hide
50829      * @method autoSize
50830      */
50831     
50832     setWidth : function()
50833     {
50834         
50835     },
50836     getResizeEl : function(){
50837         return this.el;
50838     }
50839 });//<script type="text/javasscript">
50840  
50841
50842 /**
50843  * @class Roo.DDView
50844  * A DnD enabled version of Roo.View.
50845  * @param {Element/String} container The Element in which to create the View.
50846  * @param {String} tpl The template string used to create the markup for each element of the View
50847  * @param {Object} config The configuration properties. These include all the config options of
50848  * {@link Roo.View} plus some specific to this class.<br>
50849  * <p>
50850  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50851  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50852  * <p>
50853  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50854 .x-view-drag-insert-above {
50855         border-top:1px dotted #3366cc;
50856 }
50857 .x-view-drag-insert-below {
50858         border-bottom:1px dotted #3366cc;
50859 }
50860 </code></pre>
50861  * 
50862  */
50863  
50864 Roo.DDView = function(container, tpl, config) {
50865     Roo.DDView.superclass.constructor.apply(this, arguments);
50866     this.getEl().setStyle("outline", "0px none");
50867     this.getEl().unselectable();
50868     if (this.dragGroup) {
50869                 this.setDraggable(this.dragGroup.split(","));
50870     }
50871     if (this.dropGroup) {
50872                 this.setDroppable(this.dropGroup.split(","));
50873     }
50874     if (this.deletable) {
50875         this.setDeletable();
50876     }
50877     this.isDirtyFlag = false;
50878         this.addEvents({
50879                 "drop" : true
50880         });
50881 };
50882
50883 Roo.extend(Roo.DDView, Roo.View, {
50884 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50885 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50886 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50887 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50888
50889         isFormField: true,
50890
50891         reset: Roo.emptyFn,
50892         
50893         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50894
50895         validate: function() {
50896                 return true;
50897         },
50898         
50899         destroy: function() {
50900                 this.purgeListeners();
50901                 this.getEl.removeAllListeners();
50902                 this.getEl().remove();
50903                 if (this.dragZone) {
50904                         if (this.dragZone.destroy) {
50905                                 this.dragZone.destroy();
50906                         }
50907                 }
50908                 if (this.dropZone) {
50909                         if (this.dropZone.destroy) {
50910                                 this.dropZone.destroy();
50911                         }
50912                 }
50913         },
50914
50915 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50916         getName: function() {
50917                 return this.name;
50918         },
50919
50920 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50921         setValue: function(v) {
50922                 if (!this.store) {
50923                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50924                 }
50925                 var data = {};
50926                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50927                 this.store.proxy = new Roo.data.MemoryProxy(data);
50928                 this.store.load();
50929         },
50930
50931 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50932         getValue: function() {
50933                 var result = '(';
50934                 this.store.each(function(rec) {
50935                         result += rec.id + ',';
50936                 });
50937                 return result.substr(0, result.length - 1) + ')';
50938         },
50939         
50940         getIds: function() {
50941                 var i = 0, result = new Array(this.store.getCount());
50942                 this.store.each(function(rec) {
50943                         result[i++] = rec.id;
50944                 });
50945                 return result;
50946         },
50947         
50948         isDirty: function() {
50949                 return this.isDirtyFlag;
50950         },
50951
50952 /**
50953  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50954  *      whole Element becomes the target, and this causes the drop gesture to append.
50955  */
50956     getTargetFromEvent : function(e) {
50957                 var target = e.getTarget();
50958                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50959                 target = target.parentNode;
50960                 }
50961                 if (!target) {
50962                         target = this.el.dom.lastChild || this.el.dom;
50963                 }
50964                 return target;
50965     },
50966
50967 /**
50968  *      Create the drag data which consists of an object which has the property "ddel" as
50969  *      the drag proxy element. 
50970  */
50971     getDragData : function(e) {
50972         var target = this.findItemFromChild(e.getTarget());
50973                 if(target) {
50974                         this.handleSelection(e);
50975                         var selNodes = this.getSelectedNodes();
50976             var dragData = {
50977                 source: this,
50978                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50979                 nodes: selNodes,
50980                 records: []
50981                         };
50982                         var selectedIndices = this.getSelectedIndexes();
50983                         for (var i = 0; i < selectedIndices.length; i++) {
50984                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50985                         }
50986                         if (selNodes.length == 1) {
50987                                 dragData.ddel = target.cloneNode(true); // the div element
50988                         } else {
50989                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50990                                 div.className = 'multi-proxy';
50991                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50992                                         div.appendChild(selNodes[i].cloneNode(true));
50993                                 }
50994                                 dragData.ddel = div;
50995                         }
50996             //console.log(dragData)
50997             //console.log(dragData.ddel.innerHTML)
50998                         return dragData;
50999                 }
51000         //console.log('nodragData')
51001                 return false;
51002     },
51003     
51004 /**     Specify to which ddGroup items in this DDView may be dragged. */
51005     setDraggable: function(ddGroup) {
51006         if (ddGroup instanceof Array) {
51007                 Roo.each(ddGroup, this.setDraggable, this);
51008                 return;
51009         }
51010         if (this.dragZone) {
51011                 this.dragZone.addToGroup(ddGroup);
51012         } else {
51013                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51014                                 containerScroll: true,
51015                                 ddGroup: ddGroup 
51016
51017                         });
51018 //                      Draggability implies selection. DragZone's mousedown selects the element.
51019                         if (!this.multiSelect) { this.singleSelect = true; }
51020
51021 //                      Wire the DragZone's handlers up to methods in *this*
51022                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51023                 }
51024     },
51025
51026 /**     Specify from which ddGroup this DDView accepts drops. */
51027     setDroppable: function(ddGroup) {
51028         if (ddGroup instanceof Array) {
51029                 Roo.each(ddGroup, this.setDroppable, this);
51030                 return;
51031         }
51032         if (this.dropZone) {
51033                 this.dropZone.addToGroup(ddGroup);
51034         } else {
51035                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51036                                 containerScroll: true,
51037                                 ddGroup: ddGroup
51038                         });
51039
51040 //                      Wire the DropZone's handlers up to methods in *this*
51041                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51042                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51043                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51044                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51045                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51046                 }
51047     },
51048
51049 /**     Decide whether to drop above or below a View node. */
51050     getDropPoint : function(e, n, dd){
51051         if (n == this.el.dom) { return "above"; }
51052                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51053                 var c = t + (b - t) / 2;
51054                 var y = Roo.lib.Event.getPageY(e);
51055                 if(y <= c) {
51056                         return "above";
51057                 }else{
51058                         return "below";
51059                 }
51060     },
51061
51062     onNodeEnter : function(n, dd, e, data){
51063                 return false;
51064     },
51065     
51066     onNodeOver : function(n, dd, e, data){
51067                 var pt = this.getDropPoint(e, n, dd);
51068                 // set the insert point style on the target node
51069                 var dragElClass = this.dropNotAllowed;
51070                 if (pt) {
51071                         var targetElClass;
51072                         if (pt == "above"){
51073                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51074                                 targetElClass = "x-view-drag-insert-above";
51075                         } else {
51076                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51077                                 targetElClass = "x-view-drag-insert-below";
51078                         }
51079                         if (this.lastInsertClass != targetElClass){
51080                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51081                                 this.lastInsertClass = targetElClass;
51082                         }
51083                 }
51084                 return dragElClass;
51085         },
51086
51087     onNodeOut : function(n, dd, e, data){
51088                 this.removeDropIndicators(n);
51089     },
51090
51091     onNodeDrop : function(n, dd, e, data){
51092         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51093                 return false;
51094         }
51095         var pt = this.getDropPoint(e, n, dd);
51096                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51097                 if (pt == "below") { insertAt++; }
51098                 for (var i = 0; i < data.records.length; i++) {
51099                         var r = data.records[i];
51100                         var dup = this.store.getById(r.id);
51101                         if (dup && (dd != this.dragZone)) {
51102                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51103                         } else {
51104                                 if (data.copy) {
51105                                         this.store.insert(insertAt++, r.copy());
51106                                 } else {
51107                                         data.source.isDirtyFlag = true;
51108                                         r.store.remove(r);
51109                                         this.store.insert(insertAt++, r);
51110                                 }
51111                                 this.isDirtyFlag = true;
51112                         }
51113                 }
51114                 this.dragZone.cachedTarget = null;
51115                 return true;
51116     },
51117
51118     removeDropIndicators : function(n){
51119                 if(n){
51120                         Roo.fly(n).removeClass([
51121                                 "x-view-drag-insert-above",
51122                                 "x-view-drag-insert-below"]);
51123                         this.lastInsertClass = "_noclass";
51124                 }
51125     },
51126
51127 /**
51128  *      Utility method. Add a delete option to the DDView's context menu.
51129  *      @param {String} imageUrl The URL of the "delete" icon image.
51130  */
51131         setDeletable: function(imageUrl) {
51132                 if (!this.singleSelect && !this.multiSelect) {
51133                         this.singleSelect = true;
51134                 }
51135                 var c = this.getContextMenu();
51136                 this.contextMenu.on("itemclick", function(item) {
51137                         switch (item.id) {
51138                                 case "delete":
51139                                         this.remove(this.getSelectedIndexes());
51140                                         break;
51141                         }
51142                 }, this);
51143                 this.contextMenu.add({
51144                         icon: imageUrl,
51145                         id: "delete",
51146                         text: 'Delete'
51147                 });
51148         },
51149         
51150 /**     Return the context menu for this DDView. */
51151         getContextMenu: function() {
51152                 if (!this.contextMenu) {
51153 //                      Create the View's context menu
51154                         this.contextMenu = new Roo.menu.Menu({
51155                                 id: this.id + "-contextmenu"
51156                         });
51157                         this.el.on("contextmenu", this.showContextMenu, this);
51158                 }
51159                 return this.contextMenu;
51160         },
51161         
51162         disableContextMenu: function() {
51163                 if (this.contextMenu) {
51164                         this.el.un("contextmenu", this.showContextMenu, this);
51165                 }
51166         },
51167
51168         showContextMenu: function(e, item) {
51169         item = this.findItemFromChild(e.getTarget());
51170                 if (item) {
51171                         e.stopEvent();
51172                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51173                         this.contextMenu.showAt(e.getXY());
51174             }
51175     },
51176
51177 /**
51178  *      Remove {@link Roo.data.Record}s at the specified indices.
51179  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51180  */
51181     remove: function(selectedIndices) {
51182                 selectedIndices = [].concat(selectedIndices);
51183                 for (var i = 0; i < selectedIndices.length; i++) {
51184                         var rec = this.store.getAt(selectedIndices[i]);
51185                         this.store.remove(rec);
51186                 }
51187     },
51188
51189 /**
51190  *      Double click fires the event, but also, if this is draggable, and there is only one other
51191  *      related DropZone, it transfers the selected node.
51192  */
51193     onDblClick : function(e){
51194         var item = this.findItemFromChild(e.getTarget());
51195         if(item){
51196             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51197                 return false;
51198             }
51199             if (this.dragGroup) {
51200                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51201                     while (targets.indexOf(this.dropZone) > -1) {
51202                             targets.remove(this.dropZone);
51203                                 }
51204                     if (targets.length == 1) {
51205                                         this.dragZone.cachedTarget = null;
51206                         var el = Roo.get(targets[0].getEl());
51207                         var box = el.getBox(true);
51208                         targets[0].onNodeDrop(el.dom, {
51209                                 target: el.dom,
51210                                 xy: [box.x, box.y + box.height - 1]
51211                         }, null, this.getDragData(e));
51212                     }
51213                 }
51214         }
51215     },
51216     
51217     handleSelection: function(e) {
51218                 this.dragZone.cachedTarget = null;
51219         var item = this.findItemFromChild(e.getTarget());
51220         if (!item) {
51221                 this.clearSelections(true);
51222                 return;
51223         }
51224                 if (item && (this.multiSelect || this.singleSelect)){
51225                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51226                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51227                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51228                                 this.unselect(item);
51229                         } else {
51230                                 this.select(item, this.multiSelect && e.ctrlKey);
51231                                 this.lastSelection = item;
51232                         }
51233                 }
51234     },
51235
51236     onItemClick : function(item, index, e){
51237                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51238                         return false;
51239                 }
51240                 return true;
51241     },
51242
51243     unselect : function(nodeInfo, suppressEvent){
51244                 var node = this.getNode(nodeInfo);
51245                 if(node && this.isSelected(node)){
51246                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51247                                 Roo.fly(node).removeClass(this.selectedClass);
51248                                 this.selections.remove(node);
51249                                 if(!suppressEvent){
51250                                         this.fireEvent("selectionchange", this, this.selections);
51251                                 }
51252                         }
51253                 }
51254     }
51255 });
51256 /*
51257  * Based on:
51258  * Ext JS Library 1.1.1
51259  * Copyright(c) 2006-2007, Ext JS, LLC.
51260  *
51261  * Originally Released Under LGPL - original licence link has changed is not relivant.
51262  *
51263  * Fork - LGPL
51264  * <script type="text/javascript">
51265  */
51266  
51267 /**
51268  * @class Roo.LayoutManager
51269  * @extends Roo.util.Observable
51270  * Base class for layout managers.
51271  */
51272 Roo.LayoutManager = function(container, config){
51273     Roo.LayoutManager.superclass.constructor.call(this);
51274     this.el = Roo.get(container);
51275     // ie scrollbar fix
51276     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51277         document.body.scroll = "no";
51278     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51279         this.el.position('relative');
51280     }
51281     this.id = this.el.id;
51282     this.el.addClass("x-layout-container");
51283     /** false to disable window resize monitoring @type Boolean */
51284     this.monitorWindowResize = true;
51285     this.regions = {};
51286     this.addEvents({
51287         /**
51288          * @event layout
51289          * Fires when a layout is performed. 
51290          * @param {Roo.LayoutManager} this
51291          */
51292         "layout" : true,
51293         /**
51294          * @event regionresized
51295          * Fires when the user resizes a region. 
51296          * @param {Roo.LayoutRegion} region The resized region
51297          * @param {Number} newSize The new size (width for east/west, height for north/south)
51298          */
51299         "regionresized" : true,
51300         /**
51301          * @event regioncollapsed
51302          * Fires when a region is collapsed. 
51303          * @param {Roo.LayoutRegion} region The collapsed region
51304          */
51305         "regioncollapsed" : true,
51306         /**
51307          * @event regionexpanded
51308          * Fires when a region is expanded.  
51309          * @param {Roo.LayoutRegion} region The expanded region
51310          */
51311         "regionexpanded" : true
51312     });
51313     this.updating = false;
51314     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51315 };
51316
51317 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51318     /**
51319      * Returns true if this layout is currently being updated
51320      * @return {Boolean}
51321      */
51322     isUpdating : function(){
51323         return this.updating; 
51324     },
51325     
51326     /**
51327      * Suspend the LayoutManager from doing auto-layouts while
51328      * making multiple add or remove calls
51329      */
51330     beginUpdate : function(){
51331         this.updating = true;    
51332     },
51333     
51334     /**
51335      * Restore auto-layouts and optionally disable the manager from performing a layout
51336      * @param {Boolean} noLayout true to disable a layout update 
51337      */
51338     endUpdate : function(noLayout){
51339         this.updating = false;
51340         if(!noLayout){
51341             this.layout();
51342         }    
51343     },
51344     
51345     layout: function(){
51346         
51347     },
51348     
51349     onRegionResized : function(region, newSize){
51350         this.fireEvent("regionresized", region, newSize);
51351         this.layout();
51352     },
51353     
51354     onRegionCollapsed : function(region){
51355         this.fireEvent("regioncollapsed", region);
51356     },
51357     
51358     onRegionExpanded : function(region){
51359         this.fireEvent("regionexpanded", region);
51360     },
51361         
51362     /**
51363      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51364      * performs box-model adjustments.
51365      * @return {Object} The size as an object {width: (the width), height: (the height)}
51366      */
51367     getViewSize : function(){
51368         var size;
51369         if(this.el.dom != document.body){
51370             size = this.el.getSize();
51371         }else{
51372             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51373         }
51374         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51375         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51376         return size;
51377     },
51378     
51379     /**
51380      * Returns the Element this layout is bound to.
51381      * @return {Roo.Element}
51382      */
51383     getEl : function(){
51384         return this.el;
51385     },
51386     
51387     /**
51388      * Returns the specified region.
51389      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51390      * @return {Roo.LayoutRegion}
51391      */
51392     getRegion : function(target){
51393         return this.regions[target.toLowerCase()];
51394     },
51395     
51396     onWindowResize : function(){
51397         if(this.monitorWindowResize){
51398             this.layout();
51399         }
51400     }
51401 });/*
51402  * Based on:
51403  * Ext JS Library 1.1.1
51404  * Copyright(c) 2006-2007, Ext JS, LLC.
51405  *
51406  * Originally Released Under LGPL - original licence link has changed is not relivant.
51407  *
51408  * Fork - LGPL
51409  * <script type="text/javascript">
51410  */
51411 /**
51412  * @class Roo.BorderLayout
51413  * @extends Roo.LayoutManager
51414  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51415  * please see: <br><br>
51416  * <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>
51417  * <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>
51418  * Example:
51419  <pre><code>
51420  var layout = new Roo.BorderLayout(document.body, {
51421     north: {
51422         initialSize: 25,
51423         titlebar: false
51424     },
51425     west: {
51426         split:true,
51427         initialSize: 200,
51428         minSize: 175,
51429         maxSize: 400,
51430         titlebar: true,
51431         collapsible: true
51432     },
51433     east: {
51434         split:true,
51435         initialSize: 202,
51436         minSize: 175,
51437         maxSize: 400,
51438         titlebar: true,
51439         collapsible: true
51440     },
51441     south: {
51442         split:true,
51443         initialSize: 100,
51444         minSize: 100,
51445         maxSize: 200,
51446         titlebar: true,
51447         collapsible: true
51448     },
51449     center: {
51450         titlebar: true,
51451         autoScroll:true,
51452         resizeTabs: true,
51453         minTabWidth: 50,
51454         preferredTabWidth: 150
51455     }
51456 });
51457
51458 // shorthand
51459 var CP = Roo.ContentPanel;
51460
51461 layout.beginUpdate();
51462 layout.add("north", new CP("north", "North"));
51463 layout.add("south", new CP("south", {title: "South", closable: true}));
51464 layout.add("west", new CP("west", {title: "West"}));
51465 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51466 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51467 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51468 layout.getRegion("center").showPanel("center1");
51469 layout.endUpdate();
51470 </code></pre>
51471
51472 <b>The container the layout is rendered into can be either the body element or any other element.
51473 If it is not the body element, the container needs to either be an absolute positioned element,
51474 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51475 the container size if it is not the body element.</b>
51476
51477 * @constructor
51478 * Create a new BorderLayout
51479 * @param {String/HTMLElement/Element} container The container this layout is bound to
51480 * @param {Object} config Configuration options
51481  */
51482 Roo.BorderLayout = function(container, config){
51483     config = config || {};
51484     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51485     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51486     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51487         var target = this.factory.validRegions[i];
51488         if(config[target]){
51489             this.addRegion(target, config[target]);
51490         }
51491     }
51492 };
51493
51494 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51495     /**
51496      * Creates and adds a new region if it doesn't already exist.
51497      * @param {String} target The target region key (north, south, east, west or center).
51498      * @param {Object} config The regions config object
51499      * @return {BorderLayoutRegion} The new region
51500      */
51501     addRegion : function(target, config){
51502         if(!this.regions[target]){
51503             var r = this.factory.create(target, this, config);
51504             this.bindRegion(target, r);
51505         }
51506         return this.regions[target];
51507     },
51508
51509     // private (kinda)
51510     bindRegion : function(name, r){
51511         this.regions[name] = r;
51512         r.on("visibilitychange", this.layout, this);
51513         r.on("paneladded", this.layout, this);
51514         r.on("panelremoved", this.layout, this);
51515         r.on("invalidated", this.layout, this);
51516         r.on("resized", this.onRegionResized, this);
51517         r.on("collapsed", this.onRegionCollapsed, this);
51518         r.on("expanded", this.onRegionExpanded, this);
51519     },
51520
51521     /**
51522      * Performs a layout update.
51523      */
51524     layout : function(){
51525         if(this.updating) {
51526             return;
51527         }
51528         var size = this.getViewSize();
51529         var w = size.width;
51530         var h = size.height;
51531         var centerW = w;
51532         var centerH = h;
51533         var centerY = 0;
51534         var centerX = 0;
51535         //var x = 0, y = 0;
51536
51537         var rs = this.regions;
51538         var north = rs["north"];
51539         var south = rs["south"]; 
51540         var west = rs["west"];
51541         var east = rs["east"];
51542         var center = rs["center"];
51543         //if(this.hideOnLayout){ // not supported anymore
51544             //c.el.setStyle("display", "none");
51545         //}
51546         if(north && north.isVisible()){
51547             var b = north.getBox();
51548             var m = north.getMargins();
51549             b.width = w - (m.left+m.right);
51550             b.x = m.left;
51551             b.y = m.top;
51552             centerY = b.height + b.y + m.bottom;
51553             centerH -= centerY;
51554             north.updateBox(this.safeBox(b));
51555         }
51556         if(south && south.isVisible()){
51557             var b = south.getBox();
51558             var m = south.getMargins();
51559             b.width = w - (m.left+m.right);
51560             b.x = m.left;
51561             var totalHeight = (b.height + m.top + m.bottom);
51562             b.y = h - totalHeight + m.top;
51563             centerH -= totalHeight;
51564             south.updateBox(this.safeBox(b));
51565         }
51566         if(west && west.isVisible()){
51567             var b = west.getBox();
51568             var m = west.getMargins();
51569             b.height = centerH - (m.top+m.bottom);
51570             b.x = m.left;
51571             b.y = centerY + m.top;
51572             var totalWidth = (b.width + m.left + m.right);
51573             centerX += totalWidth;
51574             centerW -= totalWidth;
51575             west.updateBox(this.safeBox(b));
51576         }
51577         if(east && east.isVisible()){
51578             var b = east.getBox();
51579             var m = east.getMargins();
51580             b.height = centerH - (m.top+m.bottom);
51581             var totalWidth = (b.width + m.left + m.right);
51582             b.x = w - totalWidth + m.left;
51583             b.y = centerY + m.top;
51584             centerW -= totalWidth;
51585             east.updateBox(this.safeBox(b));
51586         }
51587         if(center){
51588             var m = center.getMargins();
51589             var centerBox = {
51590                 x: centerX + m.left,
51591                 y: centerY + m.top,
51592                 width: centerW - (m.left+m.right),
51593                 height: centerH - (m.top+m.bottom)
51594             };
51595             //if(this.hideOnLayout){
51596                 //center.el.setStyle("display", "block");
51597             //}
51598             center.updateBox(this.safeBox(centerBox));
51599         }
51600         this.el.repaint();
51601         this.fireEvent("layout", this);
51602     },
51603
51604     // private
51605     safeBox : function(box){
51606         box.width = Math.max(0, box.width);
51607         box.height = Math.max(0, box.height);
51608         return box;
51609     },
51610
51611     /**
51612      * Adds a ContentPanel (or subclass) to this layout.
51613      * @param {String} target The target region key (north, south, east, west or center).
51614      * @param {Roo.ContentPanel} panel The panel to add
51615      * @return {Roo.ContentPanel} The added panel
51616      */
51617     add : function(target, panel){
51618          
51619         target = target.toLowerCase();
51620         return this.regions[target].add(panel);
51621     },
51622
51623     /**
51624      * Remove a ContentPanel (or subclass) to this layout.
51625      * @param {String} target The target region key (north, south, east, west or center).
51626      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51627      * @return {Roo.ContentPanel} The removed panel
51628      */
51629     remove : function(target, panel){
51630         target = target.toLowerCase();
51631         return this.regions[target].remove(panel);
51632     },
51633
51634     /**
51635      * Searches all regions for a panel with the specified id
51636      * @param {String} panelId
51637      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51638      */
51639     findPanel : function(panelId){
51640         var rs = this.regions;
51641         for(var target in rs){
51642             if(typeof rs[target] != "function"){
51643                 var p = rs[target].getPanel(panelId);
51644                 if(p){
51645                     return p;
51646                 }
51647             }
51648         }
51649         return null;
51650     },
51651
51652     /**
51653      * Searches all regions for a panel with the specified id and activates (shows) it.
51654      * @param {String/ContentPanel} panelId The panels id or the panel itself
51655      * @return {Roo.ContentPanel} The shown panel or null
51656      */
51657     showPanel : function(panelId) {
51658       var rs = this.regions;
51659       for(var target in rs){
51660          var r = rs[target];
51661          if(typeof r != "function"){
51662             if(r.hasPanel(panelId)){
51663                return r.showPanel(panelId);
51664             }
51665          }
51666       }
51667       return null;
51668    },
51669
51670    /**
51671      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51672      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51673      */
51674     restoreState : function(provider){
51675         if(!provider){
51676             provider = Roo.state.Manager;
51677         }
51678         var sm = new Roo.LayoutStateManager();
51679         sm.init(this, provider);
51680     },
51681
51682     /**
51683      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51684      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51685      * a valid ContentPanel config object.  Example:
51686      * <pre><code>
51687 // Create the main layout
51688 var layout = new Roo.BorderLayout('main-ct', {
51689     west: {
51690         split:true,
51691         minSize: 175,
51692         titlebar: true
51693     },
51694     center: {
51695         title:'Components'
51696     }
51697 }, 'main-ct');
51698
51699 // Create and add multiple ContentPanels at once via configs
51700 layout.batchAdd({
51701    west: {
51702        id: 'source-files',
51703        autoCreate:true,
51704        title:'Ext Source Files',
51705        autoScroll:true,
51706        fitToFrame:true
51707    },
51708    center : {
51709        el: cview,
51710        autoScroll:true,
51711        fitToFrame:true,
51712        toolbar: tb,
51713        resizeEl:'cbody'
51714    }
51715 });
51716 </code></pre>
51717      * @param {Object} regions An object containing ContentPanel configs by region name
51718      */
51719     batchAdd : function(regions){
51720         this.beginUpdate();
51721         for(var rname in regions){
51722             var lr = this.regions[rname];
51723             if(lr){
51724                 this.addTypedPanels(lr, regions[rname]);
51725             }
51726         }
51727         this.endUpdate();
51728     },
51729
51730     // private
51731     addTypedPanels : function(lr, ps){
51732         if(typeof ps == 'string'){
51733             lr.add(new Roo.ContentPanel(ps));
51734         }
51735         else if(ps instanceof Array){
51736             for(var i =0, len = ps.length; i < len; i++){
51737                 this.addTypedPanels(lr, ps[i]);
51738             }
51739         }
51740         else if(!ps.events){ // raw config?
51741             var el = ps.el;
51742             delete ps.el; // prevent conflict
51743             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51744         }
51745         else {  // panel object assumed!
51746             lr.add(ps);
51747         }
51748     },
51749     /**
51750      * Adds a xtype elements to the layout.
51751      * <pre><code>
51752
51753 layout.addxtype({
51754        xtype : 'ContentPanel',
51755        region: 'west',
51756        items: [ .... ]
51757    }
51758 );
51759
51760 layout.addxtype({
51761         xtype : 'NestedLayoutPanel',
51762         region: 'west',
51763         layout: {
51764            center: { },
51765            west: { }   
51766         },
51767         items : [ ... list of content panels or nested layout panels.. ]
51768    }
51769 );
51770 </code></pre>
51771      * @param {Object} cfg Xtype definition of item to add.
51772      */
51773     addxtype : function(cfg)
51774     {
51775         // basically accepts a pannel...
51776         // can accept a layout region..!?!?
51777         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51778         
51779         if (!cfg.xtype.match(/Panel$/)) {
51780             return false;
51781         }
51782         var ret = false;
51783         
51784         if (typeof(cfg.region) == 'undefined') {
51785             Roo.log("Failed to add Panel, region was not set");
51786             Roo.log(cfg);
51787             return false;
51788         }
51789         var region = cfg.region;
51790         delete cfg.region;
51791         
51792           
51793         var xitems = [];
51794         if (cfg.items) {
51795             xitems = cfg.items;
51796             delete cfg.items;
51797         }
51798         var nb = false;
51799         
51800         switch(cfg.xtype) 
51801         {
51802             case 'ContentPanel':  // ContentPanel (el, cfg)
51803             case 'ScrollPanel':  // ContentPanel (el, cfg)
51804             case 'ViewPanel': 
51805                 if(cfg.autoCreate) {
51806                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51807                 } else {
51808                     var el = this.el.createChild();
51809                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51810                 }
51811                 
51812                 this.add(region, ret);
51813                 break;
51814             
51815             
51816             case 'TreePanel': // our new panel!
51817                 cfg.el = this.el.createChild();
51818                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51819                 this.add(region, ret);
51820                 break;
51821             
51822             case 'NestedLayoutPanel': 
51823                 // create a new Layout (which is  a Border Layout...
51824                 var el = this.el.createChild();
51825                 var clayout = cfg.layout;
51826                 delete cfg.layout;
51827                 clayout.items   = clayout.items  || [];
51828                 // replace this exitems with the clayout ones..
51829                 xitems = clayout.items;
51830                  
51831                 
51832                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51833                     cfg.background = false;
51834                 }
51835                 var layout = new Roo.BorderLayout(el, clayout);
51836                 
51837                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51838                 //console.log('adding nested layout panel '  + cfg.toSource());
51839                 this.add(region, ret);
51840                 nb = {}; /// find first...
51841                 break;
51842                 
51843             case 'GridPanel': 
51844             
51845                 // needs grid and region
51846                 
51847                 //var el = this.getRegion(region).el.createChild();
51848                 var el = this.el.createChild();
51849                 // create the grid first...
51850                 
51851                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51852                 delete cfg.grid;
51853                 if (region == 'center' && this.active ) {
51854                     cfg.background = false;
51855                 }
51856                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51857                 
51858                 this.add(region, ret);
51859                 if (cfg.background) {
51860                     ret.on('activate', function(gp) {
51861                         if (!gp.grid.rendered) {
51862                             gp.grid.render();
51863                         }
51864                     });
51865                 } else {
51866                     grid.render();
51867                 }
51868                 break;
51869            
51870            
51871            
51872                 
51873                 
51874                 
51875             default:
51876                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51877                     
51878                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51879                     this.add(region, ret);
51880                 } else {
51881                 
51882                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51883                     return null;
51884                 }
51885                 
51886              // GridPanel (grid, cfg)
51887             
51888         }
51889         this.beginUpdate();
51890         // add children..
51891         var region = '';
51892         var abn = {};
51893         Roo.each(xitems, function(i)  {
51894             region = nb && i.region ? i.region : false;
51895             
51896             var add = ret.addxtype(i);
51897            
51898             if (region) {
51899                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51900                 if (!i.background) {
51901                     abn[region] = nb[region] ;
51902                 }
51903             }
51904             
51905         });
51906         this.endUpdate();
51907
51908         // make the last non-background panel active..
51909         //if (nb) { Roo.log(abn); }
51910         if (nb) {
51911             
51912             for(var r in abn) {
51913                 region = this.getRegion(r);
51914                 if (region) {
51915                     // tried using nb[r], but it does not work..
51916                      
51917                     region.showPanel(abn[r]);
51918                    
51919                 }
51920             }
51921         }
51922         return ret;
51923         
51924     }
51925 });
51926
51927 /**
51928  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51929  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51930  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51931  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51932  * <pre><code>
51933 // shorthand
51934 var CP = Roo.ContentPanel;
51935
51936 var layout = Roo.BorderLayout.create({
51937     north: {
51938         initialSize: 25,
51939         titlebar: false,
51940         panels: [new CP("north", "North")]
51941     },
51942     west: {
51943         split:true,
51944         initialSize: 200,
51945         minSize: 175,
51946         maxSize: 400,
51947         titlebar: true,
51948         collapsible: true,
51949         panels: [new CP("west", {title: "West"})]
51950     },
51951     east: {
51952         split:true,
51953         initialSize: 202,
51954         minSize: 175,
51955         maxSize: 400,
51956         titlebar: true,
51957         collapsible: true,
51958         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51959     },
51960     south: {
51961         split:true,
51962         initialSize: 100,
51963         minSize: 100,
51964         maxSize: 200,
51965         titlebar: true,
51966         collapsible: true,
51967         panels: [new CP("south", {title: "South", closable: true})]
51968     },
51969     center: {
51970         titlebar: true,
51971         autoScroll:true,
51972         resizeTabs: true,
51973         minTabWidth: 50,
51974         preferredTabWidth: 150,
51975         panels: [
51976             new CP("center1", {title: "Close Me", closable: true}),
51977             new CP("center2", {title: "Center Panel", closable: false})
51978         ]
51979     }
51980 }, document.body);
51981
51982 layout.getRegion("center").showPanel("center1");
51983 </code></pre>
51984  * @param config
51985  * @param targetEl
51986  */
51987 Roo.BorderLayout.create = function(config, targetEl){
51988     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51989     layout.beginUpdate();
51990     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51991     for(var j = 0, jlen = regions.length; j < jlen; j++){
51992         var lr = regions[j];
51993         if(layout.regions[lr] && config[lr].panels){
51994             var r = layout.regions[lr];
51995             var ps = config[lr].panels;
51996             layout.addTypedPanels(r, ps);
51997         }
51998     }
51999     layout.endUpdate();
52000     return layout;
52001 };
52002
52003 // private
52004 Roo.BorderLayout.RegionFactory = {
52005     // private
52006     validRegions : ["north","south","east","west","center"],
52007
52008     // private
52009     create : function(target, mgr, config){
52010         target = target.toLowerCase();
52011         if(config.lightweight || config.basic){
52012             return new Roo.BasicLayoutRegion(mgr, config, target);
52013         }
52014         switch(target){
52015             case "north":
52016                 return new Roo.NorthLayoutRegion(mgr, config);
52017             case "south":
52018                 return new Roo.SouthLayoutRegion(mgr, config);
52019             case "east":
52020                 return new Roo.EastLayoutRegion(mgr, config);
52021             case "west":
52022                 return new Roo.WestLayoutRegion(mgr, config);
52023             case "center":
52024                 return new Roo.CenterLayoutRegion(mgr, config);
52025         }
52026         throw 'Layout region "'+target+'" not supported.';
52027     }
52028 };/*
52029  * Based on:
52030  * Ext JS Library 1.1.1
52031  * Copyright(c) 2006-2007, Ext JS, LLC.
52032  *
52033  * Originally Released Under LGPL - original licence link has changed is not relivant.
52034  *
52035  * Fork - LGPL
52036  * <script type="text/javascript">
52037  */
52038  
52039 /**
52040  * @class Roo.BasicLayoutRegion
52041  * @extends Roo.util.Observable
52042  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52043  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52044  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52045  */
52046 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52047     this.mgr = mgr;
52048     this.position  = pos;
52049     this.events = {
52050         /**
52051          * @scope Roo.BasicLayoutRegion
52052          */
52053         
52054         /**
52055          * @event beforeremove
52056          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52057          * @param {Roo.LayoutRegion} this
52058          * @param {Roo.ContentPanel} panel The panel
52059          * @param {Object} e The cancel event object
52060          */
52061         "beforeremove" : true,
52062         /**
52063          * @event invalidated
52064          * Fires when the layout for this region is changed.
52065          * @param {Roo.LayoutRegion} this
52066          */
52067         "invalidated" : true,
52068         /**
52069          * @event visibilitychange
52070          * Fires when this region is shown or hidden 
52071          * @param {Roo.LayoutRegion} this
52072          * @param {Boolean} visibility true or false
52073          */
52074         "visibilitychange" : true,
52075         /**
52076          * @event paneladded
52077          * Fires when a panel is added. 
52078          * @param {Roo.LayoutRegion} this
52079          * @param {Roo.ContentPanel} panel The panel
52080          */
52081         "paneladded" : true,
52082         /**
52083          * @event panelremoved
52084          * Fires when a panel is removed. 
52085          * @param {Roo.LayoutRegion} this
52086          * @param {Roo.ContentPanel} panel The panel
52087          */
52088         "panelremoved" : true,
52089         /**
52090          * @event beforecollapse
52091          * Fires when this region before collapse.
52092          * @param {Roo.LayoutRegion} this
52093          */
52094         "beforecollapse" : true,
52095         /**
52096          * @event collapsed
52097          * Fires when this region is collapsed.
52098          * @param {Roo.LayoutRegion} this
52099          */
52100         "collapsed" : true,
52101         /**
52102          * @event expanded
52103          * Fires when this region is expanded.
52104          * @param {Roo.LayoutRegion} this
52105          */
52106         "expanded" : true,
52107         /**
52108          * @event slideshow
52109          * Fires when this region is slid into view.
52110          * @param {Roo.LayoutRegion} this
52111          */
52112         "slideshow" : true,
52113         /**
52114          * @event slidehide
52115          * Fires when this region slides out of view. 
52116          * @param {Roo.LayoutRegion} this
52117          */
52118         "slidehide" : true,
52119         /**
52120          * @event panelactivated
52121          * Fires when a panel is activated. 
52122          * @param {Roo.LayoutRegion} this
52123          * @param {Roo.ContentPanel} panel The activated panel
52124          */
52125         "panelactivated" : true,
52126         /**
52127          * @event resized
52128          * Fires when the user resizes this region. 
52129          * @param {Roo.LayoutRegion} this
52130          * @param {Number} newSize The new size (width for east/west, height for north/south)
52131          */
52132         "resized" : true
52133     };
52134     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52135     this.panels = new Roo.util.MixedCollection();
52136     this.panels.getKey = this.getPanelId.createDelegate(this);
52137     this.box = null;
52138     this.activePanel = null;
52139     // ensure listeners are added...
52140     
52141     if (config.listeners || config.events) {
52142         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52143             listeners : config.listeners || {},
52144             events : config.events || {}
52145         });
52146     }
52147     
52148     if(skipConfig !== true){
52149         this.applyConfig(config);
52150     }
52151 };
52152
52153 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52154     getPanelId : function(p){
52155         return p.getId();
52156     },
52157     
52158     applyConfig : function(config){
52159         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52160         this.config = config;
52161         
52162     },
52163     
52164     /**
52165      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52166      * the width, for horizontal (north, south) the height.
52167      * @param {Number} newSize The new width or height
52168      */
52169     resizeTo : function(newSize){
52170         var el = this.el ? this.el :
52171                  (this.activePanel ? this.activePanel.getEl() : null);
52172         if(el){
52173             switch(this.position){
52174                 case "east":
52175                 case "west":
52176                     el.setWidth(newSize);
52177                     this.fireEvent("resized", this, newSize);
52178                 break;
52179                 case "north":
52180                 case "south":
52181                     el.setHeight(newSize);
52182                     this.fireEvent("resized", this, newSize);
52183                 break;                
52184             }
52185         }
52186     },
52187     
52188     getBox : function(){
52189         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52190     },
52191     
52192     getMargins : function(){
52193         return this.margins;
52194     },
52195     
52196     updateBox : function(box){
52197         this.box = box;
52198         var el = this.activePanel.getEl();
52199         el.dom.style.left = box.x + "px";
52200         el.dom.style.top = box.y + "px";
52201         this.activePanel.setSize(box.width, box.height);
52202     },
52203     
52204     /**
52205      * Returns the container element for this region.
52206      * @return {Roo.Element}
52207      */
52208     getEl : function(){
52209         return this.activePanel;
52210     },
52211     
52212     /**
52213      * Returns true if this region is currently visible.
52214      * @return {Boolean}
52215      */
52216     isVisible : function(){
52217         return this.activePanel ? true : false;
52218     },
52219     
52220     setActivePanel : function(panel){
52221         panel = this.getPanel(panel);
52222         if(this.activePanel && this.activePanel != panel){
52223             this.activePanel.setActiveState(false);
52224             this.activePanel.getEl().setLeftTop(-10000,-10000);
52225         }
52226         this.activePanel = panel;
52227         panel.setActiveState(true);
52228         if(this.box){
52229             panel.setSize(this.box.width, this.box.height);
52230         }
52231         this.fireEvent("panelactivated", this, panel);
52232         this.fireEvent("invalidated");
52233     },
52234     
52235     /**
52236      * Show the specified panel.
52237      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52238      * @return {Roo.ContentPanel} The shown panel or null
52239      */
52240     showPanel : function(panel){
52241         if(panel = this.getPanel(panel)){
52242             this.setActivePanel(panel);
52243         }
52244         return panel;
52245     },
52246     
52247     /**
52248      * Get the active panel for this region.
52249      * @return {Roo.ContentPanel} The active panel or null
52250      */
52251     getActivePanel : function(){
52252         return this.activePanel;
52253     },
52254     
52255     /**
52256      * Add the passed ContentPanel(s)
52257      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52258      * @return {Roo.ContentPanel} The panel added (if only one was added)
52259      */
52260     add : function(panel){
52261         if(arguments.length > 1){
52262             for(var i = 0, len = arguments.length; i < len; i++) {
52263                 this.add(arguments[i]);
52264             }
52265             return null;
52266         }
52267         if(this.hasPanel(panel)){
52268             this.showPanel(panel);
52269             return panel;
52270         }
52271         var el = panel.getEl();
52272         if(el.dom.parentNode != this.mgr.el.dom){
52273             this.mgr.el.dom.appendChild(el.dom);
52274         }
52275         if(panel.setRegion){
52276             panel.setRegion(this);
52277         }
52278         this.panels.add(panel);
52279         el.setStyle("position", "absolute");
52280         if(!panel.background){
52281             this.setActivePanel(panel);
52282             if(this.config.initialSize && this.panels.getCount()==1){
52283                 this.resizeTo(this.config.initialSize);
52284             }
52285         }
52286         this.fireEvent("paneladded", this, panel);
52287         return panel;
52288     },
52289     
52290     /**
52291      * Returns true if the panel is in this region.
52292      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52293      * @return {Boolean}
52294      */
52295     hasPanel : function(panel){
52296         if(typeof panel == "object"){ // must be panel obj
52297             panel = panel.getId();
52298         }
52299         return this.getPanel(panel) ? true : false;
52300     },
52301     
52302     /**
52303      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52304      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52305      * @param {Boolean} preservePanel Overrides the config preservePanel option
52306      * @return {Roo.ContentPanel} The panel that was removed
52307      */
52308     remove : function(panel, preservePanel){
52309         panel = this.getPanel(panel);
52310         if(!panel){
52311             return null;
52312         }
52313         var e = {};
52314         this.fireEvent("beforeremove", this, panel, e);
52315         if(e.cancel === true){
52316             return null;
52317         }
52318         var panelId = panel.getId();
52319         this.panels.removeKey(panelId);
52320         return panel;
52321     },
52322     
52323     /**
52324      * Returns the panel specified or null if it's not in this region.
52325      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52326      * @return {Roo.ContentPanel}
52327      */
52328     getPanel : function(id){
52329         if(typeof id == "object"){ // must be panel obj
52330             return id;
52331         }
52332         return this.panels.get(id);
52333     },
52334     
52335     /**
52336      * Returns this regions position (north/south/east/west/center).
52337      * @return {String} 
52338      */
52339     getPosition: function(){
52340         return this.position;    
52341     }
52342 });/*
52343  * Based on:
52344  * Ext JS Library 1.1.1
52345  * Copyright(c) 2006-2007, Ext JS, LLC.
52346  *
52347  * Originally Released Under LGPL - original licence link has changed is not relivant.
52348  *
52349  * Fork - LGPL
52350  * <script type="text/javascript">
52351  */
52352  
52353 /**
52354  * @class Roo.LayoutRegion
52355  * @extends Roo.BasicLayoutRegion
52356  * This class represents a region in a layout manager.
52357  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52358  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52359  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52360  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52361  * @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})
52362  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52363  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52364  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52365  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52366  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52367  * @cfg {String}    title           The title for the region (overrides panel titles)
52368  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52369  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52370  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52371  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52372  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52373  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52374  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52375  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52376  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52377  * @cfg {Boolean}   showPin         True to show a pin button
52378  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52379  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52380  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52381  * @cfg {Number}    width           For East/West panels
52382  * @cfg {Number}    height          For North/South panels
52383  * @cfg {Boolean}   split           To show the splitter
52384  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52385  */
52386 Roo.LayoutRegion = function(mgr, config, pos){
52387     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52388     var dh = Roo.DomHelper;
52389     /** This region's container element 
52390     * @type Roo.Element */
52391     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52392     /** This region's title element 
52393     * @type Roo.Element */
52394
52395     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52396         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52397         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52398     ]}, true);
52399     this.titleEl.enableDisplayMode();
52400     /** This region's title text element 
52401     * @type HTMLElement */
52402     this.titleTextEl = this.titleEl.dom.firstChild;
52403     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52404     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52405     this.closeBtn.enableDisplayMode();
52406     this.closeBtn.on("click", this.closeClicked, this);
52407     this.closeBtn.hide();
52408
52409     this.createBody(config);
52410     this.visible = true;
52411     this.collapsed = false;
52412
52413     if(config.hideWhenEmpty){
52414         this.hide();
52415         this.on("paneladded", this.validateVisibility, this);
52416         this.on("panelremoved", this.validateVisibility, this);
52417     }
52418     this.applyConfig(config);
52419 };
52420
52421 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52422
52423     createBody : function(){
52424         /** This region's body element 
52425         * @type Roo.Element */
52426         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52427     },
52428
52429     applyConfig : function(c){
52430         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52431             var dh = Roo.DomHelper;
52432             if(c.titlebar !== false){
52433                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52434                 this.collapseBtn.on("click", this.collapse, this);
52435                 this.collapseBtn.enableDisplayMode();
52436
52437                 if(c.showPin === true || this.showPin){
52438                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52439                     this.stickBtn.enableDisplayMode();
52440                     this.stickBtn.on("click", this.expand, this);
52441                     this.stickBtn.hide();
52442                 }
52443             }
52444             /** This region's collapsed element
52445             * @type Roo.Element */
52446             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52447                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52448             ]}, true);
52449             if(c.floatable !== false){
52450                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52451                this.collapsedEl.on("click", this.collapseClick, this);
52452             }
52453
52454             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52455                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52456                    id: "message", unselectable: "on", style:{"float":"left"}});
52457                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52458              }
52459             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52460             this.expandBtn.on("click", this.expand, this);
52461         }
52462         if(this.collapseBtn){
52463             this.collapseBtn.setVisible(c.collapsible == true);
52464         }
52465         this.cmargins = c.cmargins || this.cmargins ||
52466                          (this.position == "west" || this.position == "east" ?
52467                              {top: 0, left: 2, right:2, bottom: 0} :
52468                              {top: 2, left: 0, right:0, bottom: 2});
52469         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52470         this.bottomTabs = c.tabPosition != "top";
52471         this.autoScroll = c.autoScroll || false;
52472         if(this.autoScroll){
52473             this.bodyEl.setStyle("overflow", "auto");
52474         }else{
52475             this.bodyEl.setStyle("overflow", "hidden");
52476         }
52477         //if(c.titlebar !== false){
52478             if((!c.titlebar && !c.title) || c.titlebar === false){
52479                 this.titleEl.hide();
52480             }else{
52481                 this.titleEl.show();
52482                 if(c.title){
52483                     this.titleTextEl.innerHTML = c.title;
52484                 }
52485             }
52486         //}
52487         this.duration = c.duration || .30;
52488         this.slideDuration = c.slideDuration || .45;
52489         this.config = c;
52490         if(c.collapsed){
52491             this.collapse(true);
52492         }
52493         if(c.hidden){
52494             this.hide();
52495         }
52496     },
52497     /**
52498      * Returns true if this region is currently visible.
52499      * @return {Boolean}
52500      */
52501     isVisible : function(){
52502         return this.visible;
52503     },
52504
52505     /**
52506      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52507      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52508      */
52509     setCollapsedTitle : function(title){
52510         title = title || "&#160;";
52511         if(this.collapsedTitleTextEl){
52512             this.collapsedTitleTextEl.innerHTML = title;
52513         }
52514     },
52515
52516     getBox : function(){
52517         var b;
52518         if(!this.collapsed){
52519             b = this.el.getBox(false, true);
52520         }else{
52521             b = this.collapsedEl.getBox(false, true);
52522         }
52523         return b;
52524     },
52525
52526     getMargins : function(){
52527         return this.collapsed ? this.cmargins : this.margins;
52528     },
52529
52530     highlight : function(){
52531         this.el.addClass("x-layout-panel-dragover");
52532     },
52533
52534     unhighlight : function(){
52535         this.el.removeClass("x-layout-panel-dragover");
52536     },
52537
52538     updateBox : function(box){
52539         this.box = box;
52540         if(!this.collapsed){
52541             this.el.dom.style.left = box.x + "px";
52542             this.el.dom.style.top = box.y + "px";
52543             this.updateBody(box.width, box.height);
52544         }else{
52545             this.collapsedEl.dom.style.left = box.x + "px";
52546             this.collapsedEl.dom.style.top = box.y + "px";
52547             this.collapsedEl.setSize(box.width, box.height);
52548         }
52549         if(this.tabs){
52550             this.tabs.autoSizeTabs();
52551         }
52552     },
52553
52554     updateBody : function(w, h){
52555         if(w !== null){
52556             this.el.setWidth(w);
52557             w -= this.el.getBorderWidth("rl");
52558             if(this.config.adjustments){
52559                 w += this.config.adjustments[0];
52560             }
52561         }
52562         if(h !== null){
52563             this.el.setHeight(h);
52564             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52565             h -= this.el.getBorderWidth("tb");
52566             if(this.config.adjustments){
52567                 h += this.config.adjustments[1];
52568             }
52569             this.bodyEl.setHeight(h);
52570             if(this.tabs){
52571                 h = this.tabs.syncHeight(h);
52572             }
52573         }
52574         if(this.panelSize){
52575             w = w !== null ? w : this.panelSize.width;
52576             h = h !== null ? h : this.panelSize.height;
52577         }
52578         if(this.activePanel){
52579             var el = this.activePanel.getEl();
52580             w = w !== null ? w : el.getWidth();
52581             h = h !== null ? h : el.getHeight();
52582             this.panelSize = {width: w, height: h};
52583             this.activePanel.setSize(w, h);
52584         }
52585         if(Roo.isIE && this.tabs){
52586             this.tabs.el.repaint();
52587         }
52588     },
52589
52590     /**
52591      * Returns the container element for this region.
52592      * @return {Roo.Element}
52593      */
52594     getEl : function(){
52595         return this.el;
52596     },
52597
52598     /**
52599      * Hides this region.
52600      */
52601     hide : function(){
52602         if(!this.collapsed){
52603             this.el.dom.style.left = "-2000px";
52604             this.el.hide();
52605         }else{
52606             this.collapsedEl.dom.style.left = "-2000px";
52607             this.collapsedEl.hide();
52608         }
52609         this.visible = false;
52610         this.fireEvent("visibilitychange", this, false);
52611     },
52612
52613     /**
52614      * Shows this region if it was previously hidden.
52615      */
52616     show : function(){
52617         if(!this.collapsed){
52618             this.el.show();
52619         }else{
52620             this.collapsedEl.show();
52621         }
52622         this.visible = true;
52623         this.fireEvent("visibilitychange", this, true);
52624     },
52625
52626     closeClicked : function(){
52627         if(this.activePanel){
52628             this.remove(this.activePanel);
52629         }
52630     },
52631
52632     collapseClick : function(e){
52633         if(this.isSlid){
52634            e.stopPropagation();
52635            this.slideIn();
52636         }else{
52637            e.stopPropagation();
52638            this.slideOut();
52639         }
52640     },
52641
52642     /**
52643      * Collapses this region.
52644      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52645      */
52646     collapse : function(skipAnim, skipCheck){
52647         if(this.collapsed) {
52648             return;
52649         }
52650         
52651         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52652             
52653             this.collapsed = true;
52654             if(this.split){
52655                 this.split.el.hide();
52656             }
52657             if(this.config.animate && skipAnim !== true){
52658                 this.fireEvent("invalidated", this);
52659                 this.animateCollapse();
52660             }else{
52661                 this.el.setLocation(-20000,-20000);
52662                 this.el.hide();
52663                 this.collapsedEl.show();
52664                 this.fireEvent("collapsed", this);
52665                 this.fireEvent("invalidated", this);
52666             }
52667         }
52668         
52669     },
52670
52671     animateCollapse : function(){
52672         // overridden
52673     },
52674
52675     /**
52676      * Expands this region if it was previously collapsed.
52677      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52678      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52679      */
52680     expand : function(e, skipAnim){
52681         if(e) {
52682             e.stopPropagation();
52683         }
52684         if(!this.collapsed || this.el.hasActiveFx()) {
52685             return;
52686         }
52687         if(this.isSlid){
52688             this.afterSlideIn();
52689             skipAnim = true;
52690         }
52691         this.collapsed = false;
52692         if(this.config.animate && skipAnim !== true){
52693             this.animateExpand();
52694         }else{
52695             this.el.show();
52696             if(this.split){
52697                 this.split.el.show();
52698             }
52699             this.collapsedEl.setLocation(-2000,-2000);
52700             this.collapsedEl.hide();
52701             this.fireEvent("invalidated", this);
52702             this.fireEvent("expanded", this);
52703         }
52704     },
52705
52706     animateExpand : function(){
52707         // overridden
52708     },
52709
52710     initTabs : function()
52711     {
52712         this.bodyEl.setStyle("overflow", "hidden");
52713         var ts = new Roo.TabPanel(
52714                 this.bodyEl.dom,
52715                 {
52716                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52717                     disableTooltips: this.config.disableTabTips,
52718                     toolbar : this.config.toolbar
52719                 }
52720         );
52721         if(this.config.hideTabs){
52722             ts.stripWrap.setDisplayed(false);
52723         }
52724         this.tabs = ts;
52725         ts.resizeTabs = this.config.resizeTabs === true;
52726         ts.minTabWidth = this.config.minTabWidth || 40;
52727         ts.maxTabWidth = this.config.maxTabWidth || 250;
52728         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52729         ts.monitorResize = false;
52730         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52731         ts.bodyEl.addClass('x-layout-tabs-body');
52732         this.panels.each(this.initPanelAsTab, this);
52733     },
52734
52735     initPanelAsTab : function(panel){
52736         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52737                     this.config.closeOnTab && panel.isClosable());
52738         if(panel.tabTip !== undefined){
52739             ti.setTooltip(panel.tabTip);
52740         }
52741         ti.on("activate", function(){
52742               this.setActivePanel(panel);
52743         }, this);
52744         if(this.config.closeOnTab){
52745             ti.on("beforeclose", function(t, e){
52746                 e.cancel = true;
52747                 this.remove(panel);
52748             }, this);
52749         }
52750         return ti;
52751     },
52752
52753     updatePanelTitle : function(panel, title){
52754         if(this.activePanel == panel){
52755             this.updateTitle(title);
52756         }
52757         if(this.tabs){
52758             var ti = this.tabs.getTab(panel.getEl().id);
52759             ti.setText(title);
52760             if(panel.tabTip !== undefined){
52761                 ti.setTooltip(panel.tabTip);
52762             }
52763         }
52764     },
52765
52766     updateTitle : function(title){
52767         if(this.titleTextEl && !this.config.title){
52768             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52769         }
52770     },
52771
52772     setActivePanel : function(panel){
52773         panel = this.getPanel(panel);
52774         if(this.activePanel && this.activePanel != panel){
52775             this.activePanel.setActiveState(false);
52776         }
52777         this.activePanel = panel;
52778         panel.setActiveState(true);
52779         if(this.panelSize){
52780             panel.setSize(this.panelSize.width, this.panelSize.height);
52781         }
52782         if(this.closeBtn){
52783             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52784         }
52785         this.updateTitle(panel.getTitle());
52786         if(this.tabs){
52787             this.fireEvent("invalidated", this);
52788         }
52789         this.fireEvent("panelactivated", this, panel);
52790     },
52791
52792     /**
52793      * Shows the specified panel.
52794      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52795      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52796      */
52797     showPanel : function(panel)
52798     {
52799         panel = this.getPanel(panel);
52800         if(panel){
52801             if(this.tabs){
52802                 var tab = this.tabs.getTab(panel.getEl().id);
52803                 if(tab.isHidden()){
52804                     this.tabs.unhideTab(tab.id);
52805                 }
52806                 tab.activate();
52807             }else{
52808                 this.setActivePanel(panel);
52809             }
52810         }
52811         return panel;
52812     },
52813
52814     /**
52815      * Get the active panel for this region.
52816      * @return {Roo.ContentPanel} The active panel or null
52817      */
52818     getActivePanel : function(){
52819         return this.activePanel;
52820     },
52821
52822     validateVisibility : function(){
52823         if(this.panels.getCount() < 1){
52824             this.updateTitle("&#160;");
52825             this.closeBtn.hide();
52826             this.hide();
52827         }else{
52828             if(!this.isVisible()){
52829                 this.show();
52830             }
52831         }
52832     },
52833
52834     /**
52835      * Adds the passed ContentPanel(s) to this region.
52836      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52837      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52838      */
52839     add : function(panel){
52840         if(arguments.length > 1){
52841             for(var i = 0, len = arguments.length; i < len; i++) {
52842                 this.add(arguments[i]);
52843             }
52844             return null;
52845         }
52846         if(this.hasPanel(panel)){
52847             this.showPanel(panel);
52848             return panel;
52849         }
52850         panel.setRegion(this);
52851         this.panels.add(panel);
52852         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52853             this.bodyEl.dom.appendChild(panel.getEl().dom);
52854             if(panel.background !== true){
52855                 this.setActivePanel(panel);
52856             }
52857             this.fireEvent("paneladded", this, panel);
52858             return panel;
52859         }
52860         if(!this.tabs){
52861             this.initTabs();
52862         }else{
52863             this.initPanelAsTab(panel);
52864         }
52865         if(panel.background !== true){
52866             this.tabs.activate(panel.getEl().id);
52867         }
52868         this.fireEvent("paneladded", this, panel);
52869         return panel;
52870     },
52871
52872     /**
52873      * Hides the tab for the specified panel.
52874      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52875      */
52876     hidePanel : function(panel){
52877         if(this.tabs && (panel = this.getPanel(panel))){
52878             this.tabs.hideTab(panel.getEl().id);
52879         }
52880     },
52881
52882     /**
52883      * Unhides the tab for a previously hidden panel.
52884      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52885      */
52886     unhidePanel : function(panel){
52887         if(this.tabs && (panel = this.getPanel(panel))){
52888             this.tabs.unhideTab(panel.getEl().id);
52889         }
52890     },
52891
52892     clearPanels : function(){
52893         while(this.panels.getCount() > 0){
52894              this.remove(this.panels.first());
52895         }
52896     },
52897
52898     /**
52899      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52900      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52901      * @param {Boolean} preservePanel Overrides the config preservePanel option
52902      * @return {Roo.ContentPanel} The panel that was removed
52903      */
52904     remove : function(panel, preservePanel){
52905         panel = this.getPanel(panel);
52906         if(!panel){
52907             return null;
52908         }
52909         var e = {};
52910         this.fireEvent("beforeremove", this, panel, e);
52911         if(e.cancel === true){
52912             return null;
52913         }
52914         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52915         var panelId = panel.getId();
52916         this.panels.removeKey(panelId);
52917         if(preservePanel){
52918             document.body.appendChild(panel.getEl().dom);
52919         }
52920         if(this.tabs){
52921             this.tabs.removeTab(panel.getEl().id);
52922         }else if (!preservePanel){
52923             this.bodyEl.dom.removeChild(panel.getEl().dom);
52924         }
52925         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52926             var p = this.panels.first();
52927             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52928             tempEl.appendChild(p.getEl().dom);
52929             this.bodyEl.update("");
52930             this.bodyEl.dom.appendChild(p.getEl().dom);
52931             tempEl = null;
52932             this.updateTitle(p.getTitle());
52933             this.tabs = null;
52934             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52935             this.setActivePanel(p);
52936         }
52937         panel.setRegion(null);
52938         if(this.activePanel == panel){
52939             this.activePanel = null;
52940         }
52941         if(this.config.autoDestroy !== false && preservePanel !== true){
52942             try{panel.destroy();}catch(e){}
52943         }
52944         this.fireEvent("panelremoved", this, panel);
52945         return panel;
52946     },
52947
52948     /**
52949      * Returns the TabPanel component used by this region
52950      * @return {Roo.TabPanel}
52951      */
52952     getTabs : function(){
52953         return this.tabs;
52954     },
52955
52956     createTool : function(parentEl, className){
52957         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52958             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52959         btn.addClassOnOver("x-layout-tools-button-over");
52960         return btn;
52961     }
52962 });/*
52963  * Based on:
52964  * Ext JS Library 1.1.1
52965  * Copyright(c) 2006-2007, Ext JS, LLC.
52966  *
52967  * Originally Released Under LGPL - original licence link has changed is not relivant.
52968  *
52969  * Fork - LGPL
52970  * <script type="text/javascript">
52971  */
52972  
52973
52974
52975 /**
52976  * @class Roo.SplitLayoutRegion
52977  * @extends Roo.LayoutRegion
52978  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52979  */
52980 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52981     this.cursor = cursor;
52982     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52983 };
52984
52985 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52986     splitTip : "Drag to resize.",
52987     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52988     useSplitTips : false,
52989
52990     applyConfig : function(config){
52991         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52992         if(config.split){
52993             if(!this.split){
52994                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52995                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52996                 /** The SplitBar for this region 
52997                 * @type Roo.SplitBar */
52998                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52999                 this.split.on("moved", this.onSplitMove, this);
53000                 this.split.useShim = config.useShim === true;
53001                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53002                 if(this.useSplitTips){
53003                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53004                 }
53005                 if(config.collapsible){
53006                     this.split.el.on("dblclick", this.collapse,  this);
53007                 }
53008             }
53009             if(typeof config.minSize != "undefined"){
53010                 this.split.minSize = config.minSize;
53011             }
53012             if(typeof config.maxSize != "undefined"){
53013                 this.split.maxSize = config.maxSize;
53014             }
53015             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53016                 this.hideSplitter();
53017             }
53018         }
53019     },
53020
53021     getHMaxSize : function(){
53022          var cmax = this.config.maxSize || 10000;
53023          var center = this.mgr.getRegion("center");
53024          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53025     },
53026
53027     getVMaxSize : function(){
53028          var cmax = this.config.maxSize || 10000;
53029          var center = this.mgr.getRegion("center");
53030          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53031     },
53032
53033     onSplitMove : function(split, newSize){
53034         this.fireEvent("resized", this, newSize);
53035     },
53036     
53037     /** 
53038      * Returns the {@link Roo.SplitBar} for this region.
53039      * @return {Roo.SplitBar}
53040      */
53041     getSplitBar : function(){
53042         return this.split;
53043     },
53044     
53045     hide : function(){
53046         this.hideSplitter();
53047         Roo.SplitLayoutRegion.superclass.hide.call(this);
53048     },
53049
53050     hideSplitter : function(){
53051         if(this.split){
53052             this.split.el.setLocation(-2000,-2000);
53053             this.split.el.hide();
53054         }
53055     },
53056
53057     show : function(){
53058         if(this.split){
53059             this.split.el.show();
53060         }
53061         Roo.SplitLayoutRegion.superclass.show.call(this);
53062     },
53063     
53064     beforeSlide: function(){
53065         if(Roo.isGecko){// firefox overflow auto bug workaround
53066             this.bodyEl.clip();
53067             if(this.tabs) {
53068                 this.tabs.bodyEl.clip();
53069             }
53070             if(this.activePanel){
53071                 this.activePanel.getEl().clip();
53072                 
53073                 if(this.activePanel.beforeSlide){
53074                     this.activePanel.beforeSlide();
53075                 }
53076             }
53077         }
53078     },
53079     
53080     afterSlide : function(){
53081         if(Roo.isGecko){// firefox overflow auto bug workaround
53082             this.bodyEl.unclip();
53083             if(this.tabs) {
53084                 this.tabs.bodyEl.unclip();
53085             }
53086             if(this.activePanel){
53087                 this.activePanel.getEl().unclip();
53088                 if(this.activePanel.afterSlide){
53089                     this.activePanel.afterSlide();
53090                 }
53091             }
53092         }
53093     },
53094
53095     initAutoHide : function(){
53096         if(this.autoHide !== false){
53097             if(!this.autoHideHd){
53098                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53099                 this.autoHideHd = {
53100                     "mouseout": function(e){
53101                         if(!e.within(this.el, true)){
53102                             st.delay(500);
53103                         }
53104                     },
53105                     "mouseover" : function(e){
53106                         st.cancel();
53107                     },
53108                     scope : this
53109                 };
53110             }
53111             this.el.on(this.autoHideHd);
53112         }
53113     },
53114
53115     clearAutoHide : function(){
53116         if(this.autoHide !== false){
53117             this.el.un("mouseout", this.autoHideHd.mouseout);
53118             this.el.un("mouseover", this.autoHideHd.mouseover);
53119         }
53120     },
53121
53122     clearMonitor : function(){
53123         Roo.get(document).un("click", this.slideInIf, this);
53124     },
53125
53126     // these names are backwards but not changed for compat
53127     slideOut : function(){
53128         if(this.isSlid || this.el.hasActiveFx()){
53129             return;
53130         }
53131         this.isSlid = true;
53132         if(this.collapseBtn){
53133             this.collapseBtn.hide();
53134         }
53135         this.closeBtnState = this.closeBtn.getStyle('display');
53136         this.closeBtn.hide();
53137         if(this.stickBtn){
53138             this.stickBtn.show();
53139         }
53140         this.el.show();
53141         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53142         this.beforeSlide();
53143         this.el.setStyle("z-index", 10001);
53144         this.el.slideIn(this.getSlideAnchor(), {
53145             callback: function(){
53146                 this.afterSlide();
53147                 this.initAutoHide();
53148                 Roo.get(document).on("click", this.slideInIf, this);
53149                 this.fireEvent("slideshow", this);
53150             },
53151             scope: this,
53152             block: true
53153         });
53154     },
53155
53156     afterSlideIn : function(){
53157         this.clearAutoHide();
53158         this.isSlid = false;
53159         this.clearMonitor();
53160         this.el.setStyle("z-index", "");
53161         if(this.collapseBtn){
53162             this.collapseBtn.show();
53163         }
53164         this.closeBtn.setStyle('display', this.closeBtnState);
53165         if(this.stickBtn){
53166             this.stickBtn.hide();
53167         }
53168         this.fireEvent("slidehide", this);
53169     },
53170
53171     slideIn : function(cb){
53172         if(!this.isSlid || this.el.hasActiveFx()){
53173             Roo.callback(cb);
53174             return;
53175         }
53176         this.isSlid = false;
53177         this.beforeSlide();
53178         this.el.slideOut(this.getSlideAnchor(), {
53179             callback: function(){
53180                 this.el.setLeftTop(-10000, -10000);
53181                 this.afterSlide();
53182                 this.afterSlideIn();
53183                 Roo.callback(cb);
53184             },
53185             scope: this,
53186             block: true
53187         });
53188     },
53189     
53190     slideInIf : function(e){
53191         if(!e.within(this.el)){
53192             this.slideIn();
53193         }
53194     },
53195
53196     animateCollapse : function(){
53197         this.beforeSlide();
53198         this.el.setStyle("z-index", 20000);
53199         var anchor = this.getSlideAnchor();
53200         this.el.slideOut(anchor, {
53201             callback : function(){
53202                 this.el.setStyle("z-index", "");
53203                 this.collapsedEl.slideIn(anchor, {duration:.3});
53204                 this.afterSlide();
53205                 this.el.setLocation(-10000,-10000);
53206                 this.el.hide();
53207                 this.fireEvent("collapsed", this);
53208             },
53209             scope: this,
53210             block: true
53211         });
53212     },
53213
53214     animateExpand : function(){
53215         this.beforeSlide();
53216         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53217         this.el.setStyle("z-index", 20000);
53218         this.collapsedEl.hide({
53219             duration:.1
53220         });
53221         this.el.slideIn(this.getSlideAnchor(), {
53222             callback : function(){
53223                 this.el.setStyle("z-index", "");
53224                 this.afterSlide();
53225                 if(this.split){
53226                     this.split.el.show();
53227                 }
53228                 this.fireEvent("invalidated", this);
53229                 this.fireEvent("expanded", this);
53230             },
53231             scope: this,
53232             block: true
53233         });
53234     },
53235
53236     anchors : {
53237         "west" : "left",
53238         "east" : "right",
53239         "north" : "top",
53240         "south" : "bottom"
53241     },
53242
53243     sanchors : {
53244         "west" : "l",
53245         "east" : "r",
53246         "north" : "t",
53247         "south" : "b"
53248     },
53249
53250     canchors : {
53251         "west" : "tl-tr",
53252         "east" : "tr-tl",
53253         "north" : "tl-bl",
53254         "south" : "bl-tl"
53255     },
53256
53257     getAnchor : function(){
53258         return this.anchors[this.position];
53259     },
53260
53261     getCollapseAnchor : function(){
53262         return this.canchors[this.position];
53263     },
53264
53265     getSlideAnchor : function(){
53266         return this.sanchors[this.position];
53267     },
53268
53269     getAlignAdj : function(){
53270         var cm = this.cmargins;
53271         switch(this.position){
53272             case "west":
53273                 return [0, 0];
53274             break;
53275             case "east":
53276                 return [0, 0];
53277             break;
53278             case "north":
53279                 return [0, 0];
53280             break;
53281             case "south":
53282                 return [0, 0];
53283             break;
53284         }
53285     },
53286
53287     getExpandAdj : function(){
53288         var c = this.collapsedEl, cm = this.cmargins;
53289         switch(this.position){
53290             case "west":
53291                 return [-(cm.right+c.getWidth()+cm.left), 0];
53292             break;
53293             case "east":
53294                 return [cm.right+c.getWidth()+cm.left, 0];
53295             break;
53296             case "north":
53297                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53298             break;
53299             case "south":
53300                 return [0, cm.top+cm.bottom+c.getHeight()];
53301             break;
53302         }
53303     }
53304 });/*
53305  * Based on:
53306  * Ext JS Library 1.1.1
53307  * Copyright(c) 2006-2007, Ext JS, LLC.
53308  *
53309  * Originally Released Under LGPL - original licence link has changed is not relivant.
53310  *
53311  * Fork - LGPL
53312  * <script type="text/javascript">
53313  */
53314 /*
53315  * These classes are private internal classes
53316  */
53317 Roo.CenterLayoutRegion = function(mgr, config){
53318     Roo.LayoutRegion.call(this, mgr, config, "center");
53319     this.visible = true;
53320     this.minWidth = config.minWidth || 20;
53321     this.minHeight = config.minHeight || 20;
53322 };
53323
53324 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53325     hide : function(){
53326         // center panel can't be hidden
53327     },
53328     
53329     show : function(){
53330         // center panel can't be hidden
53331     },
53332     
53333     getMinWidth: function(){
53334         return this.minWidth;
53335     },
53336     
53337     getMinHeight: function(){
53338         return this.minHeight;
53339     }
53340 });
53341
53342
53343 Roo.NorthLayoutRegion = function(mgr, config){
53344     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53345     if(this.split){
53346         this.split.placement = Roo.SplitBar.TOP;
53347         this.split.orientation = Roo.SplitBar.VERTICAL;
53348         this.split.el.addClass("x-layout-split-v");
53349     }
53350     var size = config.initialSize || config.height;
53351     if(typeof size != "undefined"){
53352         this.el.setHeight(size);
53353     }
53354 };
53355 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53356     orientation: Roo.SplitBar.VERTICAL,
53357     getBox : function(){
53358         if(this.collapsed){
53359             return this.collapsedEl.getBox();
53360         }
53361         var box = this.el.getBox();
53362         if(this.split){
53363             box.height += this.split.el.getHeight();
53364         }
53365         return box;
53366     },
53367     
53368     updateBox : function(box){
53369         if(this.split && !this.collapsed){
53370             box.height -= this.split.el.getHeight();
53371             this.split.el.setLeft(box.x);
53372             this.split.el.setTop(box.y+box.height);
53373             this.split.el.setWidth(box.width);
53374         }
53375         if(this.collapsed){
53376             this.updateBody(box.width, null);
53377         }
53378         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53379     }
53380 });
53381
53382 Roo.SouthLayoutRegion = function(mgr, config){
53383     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53384     if(this.split){
53385         this.split.placement = Roo.SplitBar.BOTTOM;
53386         this.split.orientation = Roo.SplitBar.VERTICAL;
53387         this.split.el.addClass("x-layout-split-v");
53388     }
53389     var size = config.initialSize || config.height;
53390     if(typeof size != "undefined"){
53391         this.el.setHeight(size);
53392     }
53393 };
53394 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53395     orientation: Roo.SplitBar.VERTICAL,
53396     getBox : function(){
53397         if(this.collapsed){
53398             return this.collapsedEl.getBox();
53399         }
53400         var box = this.el.getBox();
53401         if(this.split){
53402             var sh = this.split.el.getHeight();
53403             box.height += sh;
53404             box.y -= sh;
53405         }
53406         return box;
53407     },
53408     
53409     updateBox : function(box){
53410         if(this.split && !this.collapsed){
53411             var sh = this.split.el.getHeight();
53412             box.height -= sh;
53413             box.y += sh;
53414             this.split.el.setLeft(box.x);
53415             this.split.el.setTop(box.y-sh);
53416             this.split.el.setWidth(box.width);
53417         }
53418         if(this.collapsed){
53419             this.updateBody(box.width, null);
53420         }
53421         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53422     }
53423 });
53424
53425 Roo.EastLayoutRegion = function(mgr, config){
53426     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53427     if(this.split){
53428         this.split.placement = Roo.SplitBar.RIGHT;
53429         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53430         this.split.el.addClass("x-layout-split-h");
53431     }
53432     var size = config.initialSize || config.width;
53433     if(typeof size != "undefined"){
53434         this.el.setWidth(size);
53435     }
53436 };
53437 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53438     orientation: Roo.SplitBar.HORIZONTAL,
53439     getBox : function(){
53440         if(this.collapsed){
53441             return this.collapsedEl.getBox();
53442         }
53443         var box = this.el.getBox();
53444         if(this.split){
53445             var sw = this.split.el.getWidth();
53446             box.width += sw;
53447             box.x -= sw;
53448         }
53449         return box;
53450     },
53451
53452     updateBox : function(box){
53453         if(this.split && !this.collapsed){
53454             var sw = this.split.el.getWidth();
53455             box.width -= sw;
53456             this.split.el.setLeft(box.x);
53457             this.split.el.setTop(box.y);
53458             this.split.el.setHeight(box.height);
53459             box.x += sw;
53460         }
53461         if(this.collapsed){
53462             this.updateBody(null, box.height);
53463         }
53464         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53465     }
53466 });
53467
53468 Roo.WestLayoutRegion = function(mgr, config){
53469     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53470     if(this.split){
53471         this.split.placement = Roo.SplitBar.LEFT;
53472         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53473         this.split.el.addClass("x-layout-split-h");
53474     }
53475     var size = config.initialSize || config.width;
53476     if(typeof size != "undefined"){
53477         this.el.setWidth(size);
53478     }
53479 };
53480 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53481     orientation: Roo.SplitBar.HORIZONTAL,
53482     getBox : function(){
53483         if(this.collapsed){
53484             return this.collapsedEl.getBox();
53485         }
53486         var box = this.el.getBox();
53487         if(this.split){
53488             box.width += this.split.el.getWidth();
53489         }
53490         return box;
53491     },
53492     
53493     updateBox : function(box){
53494         if(this.split && !this.collapsed){
53495             var sw = this.split.el.getWidth();
53496             box.width -= sw;
53497             this.split.el.setLeft(box.x+box.width);
53498             this.split.el.setTop(box.y);
53499             this.split.el.setHeight(box.height);
53500         }
53501         if(this.collapsed){
53502             this.updateBody(null, box.height);
53503         }
53504         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53505     }
53506 });
53507 /*
53508  * Based on:
53509  * Ext JS Library 1.1.1
53510  * Copyright(c) 2006-2007, Ext JS, LLC.
53511  *
53512  * Originally Released Under LGPL - original licence link has changed is not relivant.
53513  *
53514  * Fork - LGPL
53515  * <script type="text/javascript">
53516  */
53517  
53518  
53519 /*
53520  * Private internal class for reading and applying state
53521  */
53522 Roo.LayoutStateManager = function(layout){
53523      // default empty state
53524      this.state = {
53525         north: {},
53526         south: {},
53527         east: {},
53528         west: {}       
53529     };
53530 };
53531
53532 Roo.LayoutStateManager.prototype = {
53533     init : function(layout, provider){
53534         this.provider = provider;
53535         var state = provider.get(layout.id+"-layout-state");
53536         if(state){
53537             var wasUpdating = layout.isUpdating();
53538             if(!wasUpdating){
53539                 layout.beginUpdate();
53540             }
53541             for(var key in state){
53542                 if(typeof state[key] != "function"){
53543                     var rstate = state[key];
53544                     var r = layout.getRegion(key);
53545                     if(r && rstate){
53546                         if(rstate.size){
53547                             r.resizeTo(rstate.size);
53548                         }
53549                         if(rstate.collapsed == true){
53550                             r.collapse(true);
53551                         }else{
53552                             r.expand(null, true);
53553                         }
53554                     }
53555                 }
53556             }
53557             if(!wasUpdating){
53558                 layout.endUpdate();
53559             }
53560             this.state = state; 
53561         }
53562         this.layout = layout;
53563         layout.on("regionresized", this.onRegionResized, this);
53564         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53565         layout.on("regionexpanded", this.onRegionExpanded, this);
53566     },
53567     
53568     storeState : function(){
53569         this.provider.set(this.layout.id+"-layout-state", this.state);
53570     },
53571     
53572     onRegionResized : function(region, newSize){
53573         this.state[region.getPosition()].size = newSize;
53574         this.storeState();
53575     },
53576     
53577     onRegionCollapsed : function(region){
53578         this.state[region.getPosition()].collapsed = true;
53579         this.storeState();
53580     },
53581     
53582     onRegionExpanded : function(region){
53583         this.state[region.getPosition()].collapsed = false;
53584         this.storeState();
53585     }
53586 };/*
53587  * Based on:
53588  * Ext JS Library 1.1.1
53589  * Copyright(c) 2006-2007, Ext JS, LLC.
53590  *
53591  * Originally Released Under LGPL - original licence link has changed is not relivant.
53592  *
53593  * Fork - LGPL
53594  * <script type="text/javascript">
53595  */
53596 /**
53597  * @class Roo.ContentPanel
53598  * @extends Roo.util.Observable
53599  * A basic ContentPanel element.
53600  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53601  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53602  * @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
53603  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53604  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53605  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53606  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53607  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53608  * @cfg {String} title          The title for this panel
53609  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53610  * @cfg {String} url            Calls {@link #setUrl} with this value
53611  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53612  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53613  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53614  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53615
53616  * @constructor
53617  * Create a new ContentPanel.
53618  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53619  * @param {String/Object} config A string to set only the title or a config object
53620  * @param {String} content (optional) Set the HTML content for this panel
53621  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53622  */
53623 Roo.ContentPanel = function(el, config, content){
53624     
53625      
53626     /*
53627     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53628         config = el;
53629         el = Roo.id();
53630     }
53631     if (config && config.parentLayout) { 
53632         el = config.parentLayout.el.createChild(); 
53633     }
53634     */
53635     if(el.autoCreate){ // xtype is available if this is called from factory
53636         config = el;
53637         el = Roo.id();
53638     }
53639     this.el = Roo.get(el);
53640     if(!this.el && config && config.autoCreate){
53641         if(typeof config.autoCreate == "object"){
53642             if(!config.autoCreate.id){
53643                 config.autoCreate.id = config.id||el;
53644             }
53645             this.el = Roo.DomHelper.append(document.body,
53646                         config.autoCreate, true);
53647         }else{
53648             this.el = Roo.DomHelper.append(document.body,
53649                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53650         }
53651     }
53652     this.closable = false;
53653     this.loaded = false;
53654     this.active = false;
53655     if(typeof config == "string"){
53656         this.title = config;
53657     }else{
53658         Roo.apply(this, config);
53659     }
53660     
53661     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53662         this.wrapEl = this.el.wrap();
53663         this.toolbar.container = this.el.insertSibling(false, 'before');
53664         this.toolbar = new Roo.Toolbar(this.toolbar);
53665     }
53666     
53667     // xtype created footer. - not sure if will work as we normally have to render first..
53668     if (this.footer && !this.footer.el && this.footer.xtype) {
53669         if (!this.wrapEl) {
53670             this.wrapEl = this.el.wrap();
53671         }
53672     
53673         this.footer.container = this.wrapEl.createChild();
53674          
53675         this.footer = Roo.factory(this.footer, Roo);
53676         
53677     }
53678     
53679     if(this.resizeEl){
53680         this.resizeEl = Roo.get(this.resizeEl, true);
53681     }else{
53682         this.resizeEl = this.el;
53683     }
53684     // handle view.xtype
53685     
53686  
53687     
53688     
53689     this.addEvents({
53690         /**
53691          * @event activate
53692          * Fires when this panel is activated. 
53693          * @param {Roo.ContentPanel} this
53694          */
53695         "activate" : true,
53696         /**
53697          * @event deactivate
53698          * Fires when this panel is activated. 
53699          * @param {Roo.ContentPanel} this
53700          */
53701         "deactivate" : true,
53702
53703         /**
53704          * @event resize
53705          * Fires when this panel is resized if fitToFrame is true.
53706          * @param {Roo.ContentPanel} this
53707          * @param {Number} width The width after any component adjustments
53708          * @param {Number} height The height after any component adjustments
53709          */
53710         "resize" : true,
53711         
53712          /**
53713          * @event render
53714          * Fires when this tab is created
53715          * @param {Roo.ContentPanel} this
53716          */
53717         "render" : true
53718          
53719         
53720     });
53721     
53722
53723     
53724     
53725     if(this.autoScroll){
53726         this.resizeEl.setStyle("overflow", "auto");
53727     } else {
53728         // fix randome scrolling
53729         this.el.on('scroll', function() {
53730             Roo.log('fix random scolling');
53731             this.scrollTo('top',0); 
53732         });
53733     }
53734     content = content || this.content;
53735     if(content){
53736         this.setContent(content);
53737     }
53738     if(config && config.url){
53739         this.setUrl(this.url, this.params, this.loadOnce);
53740     }
53741     
53742     
53743     
53744     Roo.ContentPanel.superclass.constructor.call(this);
53745     
53746     if (this.view && typeof(this.view.xtype) != 'undefined') {
53747         this.view.el = this.el.appendChild(document.createElement("div"));
53748         this.view = Roo.factory(this.view); 
53749         this.view.render  &&  this.view.render(false, '');  
53750     }
53751     
53752     
53753     this.fireEvent('render', this);
53754 };
53755
53756 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53757     tabTip:'',
53758     setRegion : function(region){
53759         this.region = region;
53760         if(region){
53761            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53762         }else{
53763            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53764         } 
53765     },
53766     
53767     /**
53768      * Returns the toolbar for this Panel if one was configured. 
53769      * @return {Roo.Toolbar} 
53770      */
53771     getToolbar : function(){
53772         return this.toolbar;
53773     },
53774     
53775     setActiveState : function(active){
53776         this.active = active;
53777         if(!active){
53778             this.fireEvent("deactivate", this);
53779         }else{
53780             this.fireEvent("activate", this);
53781         }
53782     },
53783     /**
53784      * Updates this panel's element
53785      * @param {String} content The new content
53786      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53787     */
53788     setContent : function(content, loadScripts){
53789         this.el.update(content, loadScripts);
53790     },
53791
53792     ignoreResize : function(w, h){
53793         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53794             return true;
53795         }else{
53796             this.lastSize = {width: w, height: h};
53797             return false;
53798         }
53799     },
53800     /**
53801      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53802      * @return {Roo.UpdateManager} The UpdateManager
53803      */
53804     getUpdateManager : function(){
53805         return this.el.getUpdateManager();
53806     },
53807      /**
53808      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53809      * @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:
53810 <pre><code>
53811 panel.load({
53812     url: "your-url.php",
53813     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53814     callback: yourFunction,
53815     scope: yourObject, //(optional scope)
53816     discardUrl: false,
53817     nocache: false,
53818     text: "Loading...",
53819     timeout: 30,
53820     scripts: false
53821 });
53822 </code></pre>
53823      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53824      * 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.
53825      * @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}
53826      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53827      * @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.
53828      * @return {Roo.ContentPanel} this
53829      */
53830     load : function(){
53831         var um = this.el.getUpdateManager();
53832         um.update.apply(um, arguments);
53833         return this;
53834     },
53835
53836
53837     /**
53838      * 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.
53839      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53840      * @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)
53841      * @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)
53842      * @return {Roo.UpdateManager} The UpdateManager
53843      */
53844     setUrl : function(url, params, loadOnce){
53845         if(this.refreshDelegate){
53846             this.removeListener("activate", this.refreshDelegate);
53847         }
53848         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53849         this.on("activate", this.refreshDelegate);
53850         return this.el.getUpdateManager();
53851     },
53852     
53853     _handleRefresh : function(url, params, loadOnce){
53854         if(!loadOnce || !this.loaded){
53855             var updater = this.el.getUpdateManager();
53856             updater.update(url, params, this._setLoaded.createDelegate(this));
53857         }
53858     },
53859     
53860     _setLoaded : function(){
53861         this.loaded = true;
53862     }, 
53863     
53864     /**
53865      * Returns this panel's id
53866      * @return {String} 
53867      */
53868     getId : function(){
53869         return this.el.id;
53870     },
53871     
53872     /** 
53873      * Returns this panel's element - used by regiosn to add.
53874      * @return {Roo.Element} 
53875      */
53876     getEl : function(){
53877         return this.wrapEl || this.el;
53878     },
53879     
53880     adjustForComponents : function(width, height)
53881     {
53882         //Roo.log('adjustForComponents ');
53883         if(this.resizeEl != this.el){
53884             width -= this.el.getFrameWidth('lr');
53885             height -= this.el.getFrameWidth('tb');
53886         }
53887         if(this.toolbar){
53888             var te = this.toolbar.getEl();
53889             height -= te.getHeight();
53890             te.setWidth(width);
53891         }
53892         if(this.footer){
53893             var te = this.footer.getEl();
53894             //Roo.log("footer:" + te.getHeight());
53895             
53896             height -= te.getHeight();
53897             te.setWidth(width);
53898         }
53899         
53900         
53901         if(this.adjustments){
53902             width += this.adjustments[0];
53903             height += this.adjustments[1];
53904         }
53905         return {"width": width, "height": height};
53906     },
53907     
53908     setSize : function(width, height){
53909         if(this.fitToFrame && !this.ignoreResize(width, height)){
53910             if(this.fitContainer && this.resizeEl != this.el){
53911                 this.el.setSize(width, height);
53912             }
53913             var size = this.adjustForComponents(width, height);
53914             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53915             this.fireEvent('resize', this, size.width, size.height);
53916         }
53917     },
53918     
53919     /**
53920      * Returns this panel's title
53921      * @return {String} 
53922      */
53923     getTitle : function(){
53924         return this.title;
53925     },
53926     
53927     /**
53928      * Set this panel's title
53929      * @param {String} title
53930      */
53931     setTitle : function(title){
53932         this.title = title;
53933         if(this.region){
53934             this.region.updatePanelTitle(this, title);
53935         }
53936     },
53937     
53938     /**
53939      * Returns true is this panel was configured to be closable
53940      * @return {Boolean} 
53941      */
53942     isClosable : function(){
53943         return this.closable;
53944     },
53945     
53946     beforeSlide : function(){
53947         this.el.clip();
53948         this.resizeEl.clip();
53949     },
53950     
53951     afterSlide : function(){
53952         this.el.unclip();
53953         this.resizeEl.unclip();
53954     },
53955     
53956     /**
53957      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53958      *   Will fail silently if the {@link #setUrl} method has not been called.
53959      *   This does not activate the panel, just updates its content.
53960      */
53961     refresh : function(){
53962         if(this.refreshDelegate){
53963            this.loaded = false;
53964            this.refreshDelegate();
53965         }
53966     },
53967     
53968     /**
53969      * Destroys this panel
53970      */
53971     destroy : function(){
53972         this.el.removeAllListeners();
53973         var tempEl = document.createElement("span");
53974         tempEl.appendChild(this.el.dom);
53975         tempEl.innerHTML = "";
53976         this.el.remove();
53977         this.el = null;
53978     },
53979     
53980     /**
53981      * form - if the content panel contains a form - this is a reference to it.
53982      * @type {Roo.form.Form}
53983      */
53984     form : false,
53985     /**
53986      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53987      *    This contains a reference to it.
53988      * @type {Roo.View}
53989      */
53990     view : false,
53991     
53992       /**
53993      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53994      * <pre><code>
53995
53996 layout.addxtype({
53997        xtype : 'Form',
53998        items: [ .... ]
53999    }
54000 );
54001
54002 </code></pre>
54003      * @param {Object} cfg Xtype definition of item to add.
54004      */
54005     
54006     addxtype : function(cfg) {
54007         // add form..
54008         if (cfg.xtype.match(/^Form$/)) {
54009             
54010             var el;
54011             //if (this.footer) {
54012             //    el = this.footer.container.insertSibling(false, 'before');
54013             //} else {
54014                 el = this.el.createChild();
54015             //}
54016
54017             this.form = new  Roo.form.Form(cfg);
54018             
54019             
54020             if ( this.form.allItems.length) {
54021                 this.form.render(el.dom);
54022             }
54023             return this.form;
54024         }
54025         // should only have one of theses..
54026         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54027             // views.. should not be just added - used named prop 'view''
54028             
54029             cfg.el = this.el.appendChild(document.createElement("div"));
54030             // factory?
54031             
54032             var ret = new Roo.factory(cfg);
54033              
54034              ret.render && ret.render(false, ''); // render blank..
54035             this.view = ret;
54036             return ret;
54037         }
54038         return false;
54039     }
54040 });
54041
54042 /**
54043  * @class Roo.GridPanel
54044  * @extends Roo.ContentPanel
54045  * @constructor
54046  * Create a new GridPanel.
54047  * @param {Roo.grid.Grid} grid The grid for this panel
54048  * @param {String/Object} config A string to set only the panel's title, or a config object
54049  */
54050 Roo.GridPanel = function(grid, config){
54051     
54052   
54053     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54054         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54055         
54056     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54057     
54058     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54059     
54060     if(this.toolbar){
54061         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54062     }
54063     // xtype created footer. - not sure if will work as we normally have to render first..
54064     if (this.footer && !this.footer.el && this.footer.xtype) {
54065         
54066         this.footer.container = this.grid.getView().getFooterPanel(true);
54067         this.footer.dataSource = this.grid.dataSource;
54068         this.footer = Roo.factory(this.footer, Roo);
54069         
54070     }
54071     
54072     grid.monitorWindowResize = false; // turn off autosizing
54073     grid.autoHeight = false;
54074     grid.autoWidth = false;
54075     this.grid = grid;
54076     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54077 };
54078
54079 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54080     getId : function(){
54081         return this.grid.id;
54082     },
54083     
54084     /**
54085      * Returns the grid for this panel
54086      * @return {Roo.grid.Grid} 
54087      */
54088     getGrid : function(){
54089         return this.grid;    
54090     },
54091     
54092     setSize : function(width, height){
54093         if(!this.ignoreResize(width, height)){
54094             var grid = this.grid;
54095             var size = this.adjustForComponents(width, height);
54096             grid.getGridEl().setSize(size.width, size.height);
54097             grid.autoSize();
54098         }
54099     },
54100     
54101     beforeSlide : function(){
54102         this.grid.getView().scroller.clip();
54103     },
54104     
54105     afterSlide : function(){
54106         this.grid.getView().scroller.unclip();
54107     },
54108     
54109     destroy : function(){
54110         this.grid.destroy();
54111         delete this.grid;
54112         Roo.GridPanel.superclass.destroy.call(this); 
54113     }
54114 });
54115
54116
54117 /**
54118  * @class Roo.NestedLayoutPanel
54119  * @extends Roo.ContentPanel
54120  * @constructor
54121  * Create a new NestedLayoutPanel.
54122  * 
54123  * 
54124  * @param {Roo.BorderLayout} layout The layout for this panel
54125  * @param {String/Object} config A string to set only the title or a config object
54126  */
54127 Roo.NestedLayoutPanel = function(layout, config)
54128 {
54129     // construct with only one argument..
54130     /* FIXME - implement nicer consturctors
54131     if (layout.layout) {
54132         config = layout;
54133         layout = config.layout;
54134         delete config.layout;
54135     }
54136     if (layout.xtype && !layout.getEl) {
54137         // then layout needs constructing..
54138         layout = Roo.factory(layout, Roo);
54139     }
54140     */
54141     
54142     
54143     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54144     
54145     layout.monitorWindowResize = false; // turn off autosizing
54146     this.layout = layout;
54147     this.layout.getEl().addClass("x-layout-nested-layout");
54148     
54149     
54150     
54151     
54152 };
54153
54154 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54155
54156     setSize : function(width, height){
54157         if(!this.ignoreResize(width, height)){
54158             var size = this.adjustForComponents(width, height);
54159             var el = this.layout.getEl();
54160             el.setSize(size.width, size.height);
54161             var touch = el.dom.offsetWidth;
54162             this.layout.layout();
54163             // ie requires a double layout on the first pass
54164             if(Roo.isIE && !this.initialized){
54165                 this.initialized = true;
54166                 this.layout.layout();
54167             }
54168         }
54169     },
54170     
54171     // activate all subpanels if not currently active..
54172     
54173     setActiveState : function(active){
54174         this.active = active;
54175         if(!active){
54176             this.fireEvent("deactivate", this);
54177             return;
54178         }
54179         
54180         this.fireEvent("activate", this);
54181         // not sure if this should happen before or after..
54182         if (!this.layout) {
54183             return; // should not happen..
54184         }
54185         var reg = false;
54186         for (var r in this.layout.regions) {
54187             reg = this.layout.getRegion(r);
54188             if (reg.getActivePanel()) {
54189                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54190                 reg.setActivePanel(reg.getActivePanel());
54191                 continue;
54192             }
54193             if (!reg.panels.length) {
54194                 continue;
54195             }
54196             reg.showPanel(reg.getPanel(0));
54197         }
54198         
54199         
54200         
54201         
54202     },
54203     
54204     /**
54205      * Returns the nested BorderLayout for this panel
54206      * @return {Roo.BorderLayout} 
54207      */
54208     getLayout : function(){
54209         return this.layout;
54210     },
54211     
54212      /**
54213      * Adds a xtype elements to the layout of the nested panel
54214      * <pre><code>
54215
54216 panel.addxtype({
54217        xtype : 'ContentPanel',
54218        region: 'west',
54219        items: [ .... ]
54220    }
54221 );
54222
54223 panel.addxtype({
54224         xtype : 'NestedLayoutPanel',
54225         region: 'west',
54226         layout: {
54227            center: { },
54228            west: { }   
54229         },
54230         items : [ ... list of content panels or nested layout panels.. ]
54231    }
54232 );
54233 </code></pre>
54234      * @param {Object} cfg Xtype definition of item to add.
54235      */
54236     addxtype : function(cfg) {
54237         return this.layout.addxtype(cfg);
54238     
54239     }
54240 });
54241
54242 Roo.ScrollPanel = function(el, config, content){
54243     config = config || {};
54244     config.fitToFrame = true;
54245     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54246     
54247     this.el.dom.style.overflow = "hidden";
54248     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54249     this.el.removeClass("x-layout-inactive-content");
54250     this.el.on("mousewheel", this.onWheel, this);
54251
54252     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54253     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54254     up.unselectable(); down.unselectable();
54255     up.on("click", this.scrollUp, this);
54256     down.on("click", this.scrollDown, this);
54257     up.addClassOnOver("x-scroller-btn-over");
54258     down.addClassOnOver("x-scroller-btn-over");
54259     up.addClassOnClick("x-scroller-btn-click");
54260     down.addClassOnClick("x-scroller-btn-click");
54261     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54262
54263     this.resizeEl = this.el;
54264     this.el = wrap; this.up = up; this.down = down;
54265 };
54266
54267 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54268     increment : 100,
54269     wheelIncrement : 5,
54270     scrollUp : function(){
54271         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54272     },
54273
54274     scrollDown : function(){
54275         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54276     },
54277
54278     afterScroll : function(){
54279         var el = this.resizeEl;
54280         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54281         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54282         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54283     },
54284
54285     setSize : function(){
54286         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54287         this.afterScroll();
54288     },
54289
54290     onWheel : function(e){
54291         var d = e.getWheelDelta();
54292         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54293         this.afterScroll();
54294         e.stopEvent();
54295     },
54296
54297     setContent : function(content, loadScripts){
54298         this.resizeEl.update(content, loadScripts);
54299     }
54300
54301 });
54302
54303
54304
54305
54306
54307
54308
54309
54310
54311 /**
54312  * @class Roo.TreePanel
54313  * @extends Roo.ContentPanel
54314  * @constructor
54315  * Create a new TreePanel. - defaults to fit/scoll contents.
54316  * @param {String/Object} config A string to set only the panel's title, or a config object
54317  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54318  */
54319 Roo.TreePanel = function(config){
54320     var el = config.el;
54321     var tree = config.tree;
54322     delete config.tree; 
54323     delete config.el; // hopefull!
54324     
54325     // wrapper for IE7 strict & safari scroll issue
54326     
54327     var treeEl = el.createChild();
54328     config.resizeEl = treeEl;
54329     
54330     
54331     
54332     Roo.TreePanel.superclass.constructor.call(this, el, config);
54333  
54334  
54335     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54336     //console.log(tree);
54337     this.on('activate', function()
54338     {
54339         if (this.tree.rendered) {
54340             return;
54341         }
54342         //console.log('render tree');
54343         this.tree.render();
54344     });
54345     // this should not be needed.. - it's actually the 'el' that resizes?
54346     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54347     
54348     //this.on('resize',  function (cp, w, h) {
54349     //        this.tree.innerCt.setWidth(w);
54350     //        this.tree.innerCt.setHeight(h);
54351     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54352     //});
54353
54354         
54355     
54356 };
54357
54358 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54359     fitToFrame : true,
54360     autoScroll : true
54361 });
54362
54363
54364
54365
54366
54367
54368
54369
54370
54371
54372
54373 /*
54374  * Based on:
54375  * Ext JS Library 1.1.1
54376  * Copyright(c) 2006-2007, Ext JS, LLC.
54377  *
54378  * Originally Released Under LGPL - original licence link has changed is not relivant.
54379  *
54380  * Fork - LGPL
54381  * <script type="text/javascript">
54382  */
54383  
54384
54385 /**
54386  * @class Roo.ReaderLayout
54387  * @extends Roo.BorderLayout
54388  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54389  * center region containing two nested regions (a top one for a list view and one for item preview below),
54390  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54391  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54392  * expedites the setup of the overall layout and regions for this common application style.
54393  * Example:
54394  <pre><code>
54395 var reader = new Roo.ReaderLayout();
54396 var CP = Roo.ContentPanel;  // shortcut for adding
54397
54398 reader.beginUpdate();
54399 reader.add("north", new CP("north", "North"));
54400 reader.add("west", new CP("west", {title: "West"}));
54401 reader.add("east", new CP("east", {title: "East"}));
54402
54403 reader.regions.listView.add(new CP("listView", "List"));
54404 reader.regions.preview.add(new CP("preview", "Preview"));
54405 reader.endUpdate();
54406 </code></pre>
54407 * @constructor
54408 * Create a new ReaderLayout
54409 * @param {Object} config Configuration options
54410 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54411 * document.body if omitted)
54412 */
54413 Roo.ReaderLayout = function(config, renderTo){
54414     var c = config || {size:{}};
54415     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54416         north: c.north !== false ? Roo.apply({
54417             split:false,
54418             initialSize: 32,
54419             titlebar: false
54420         }, c.north) : false,
54421         west: c.west !== false ? Roo.apply({
54422             split:true,
54423             initialSize: 200,
54424             minSize: 175,
54425             maxSize: 400,
54426             titlebar: true,
54427             collapsible: true,
54428             animate: true,
54429             margins:{left:5,right:0,bottom:5,top:5},
54430             cmargins:{left:5,right:5,bottom:5,top:5}
54431         }, c.west) : false,
54432         east: c.east !== false ? Roo.apply({
54433             split:true,
54434             initialSize: 200,
54435             minSize: 175,
54436             maxSize: 400,
54437             titlebar: true,
54438             collapsible: true,
54439             animate: true,
54440             margins:{left:0,right:5,bottom:5,top:5},
54441             cmargins:{left:5,right:5,bottom:5,top:5}
54442         }, c.east) : false,
54443         center: Roo.apply({
54444             tabPosition: 'top',
54445             autoScroll:false,
54446             closeOnTab: true,
54447             titlebar:false,
54448             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54449         }, c.center)
54450     });
54451
54452     this.el.addClass('x-reader');
54453
54454     this.beginUpdate();
54455
54456     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54457         south: c.preview !== false ? Roo.apply({
54458             split:true,
54459             initialSize: 200,
54460             minSize: 100,
54461             autoScroll:true,
54462             collapsible:true,
54463             titlebar: true,
54464             cmargins:{top:5,left:0, right:0, bottom:0}
54465         }, c.preview) : false,
54466         center: Roo.apply({
54467             autoScroll:false,
54468             titlebar:false,
54469             minHeight:200
54470         }, c.listView)
54471     });
54472     this.add('center', new Roo.NestedLayoutPanel(inner,
54473             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54474
54475     this.endUpdate();
54476
54477     this.regions.preview = inner.getRegion('south');
54478     this.regions.listView = inner.getRegion('center');
54479 };
54480
54481 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54482  * Based on:
54483  * Ext JS Library 1.1.1
54484  * Copyright(c) 2006-2007, Ext JS, LLC.
54485  *
54486  * Originally Released Under LGPL - original licence link has changed is not relivant.
54487  *
54488  * Fork - LGPL
54489  * <script type="text/javascript">
54490  */
54491  
54492 /**
54493  * @class Roo.grid.Grid
54494  * @extends Roo.util.Observable
54495  * This class represents the primary interface of a component based grid control.
54496  * <br><br>Usage:<pre><code>
54497  var grid = new Roo.grid.Grid("my-container-id", {
54498      ds: myDataStore,
54499      cm: myColModel,
54500      selModel: mySelectionModel,
54501      autoSizeColumns: true,
54502      monitorWindowResize: false,
54503      trackMouseOver: true
54504  });
54505  // set any options
54506  grid.render();
54507  * </code></pre>
54508  * <b>Common Problems:</b><br/>
54509  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54510  * element will correct this<br/>
54511  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54512  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54513  * are unpredictable.<br/>
54514  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54515  * grid to calculate dimensions/offsets.<br/>
54516   * @constructor
54517  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54518  * The container MUST have some type of size defined for the grid to fill. The container will be
54519  * automatically set to position relative if it isn't already.
54520  * @param {Object} config A config object that sets properties on this grid.
54521  */
54522 Roo.grid.Grid = function(container, config){
54523         // initialize the container
54524         this.container = Roo.get(container);
54525         this.container.update("");
54526         this.container.setStyle("overflow", "hidden");
54527     this.container.addClass('x-grid-container');
54528
54529     this.id = this.container.id;
54530
54531     Roo.apply(this, config);
54532     // check and correct shorthanded configs
54533     if(this.ds){
54534         this.dataSource = this.ds;
54535         delete this.ds;
54536     }
54537     if(this.cm){
54538         this.colModel = this.cm;
54539         delete this.cm;
54540     }
54541     if(this.sm){
54542         this.selModel = this.sm;
54543         delete this.sm;
54544     }
54545
54546     if (this.selModel) {
54547         this.selModel = Roo.factory(this.selModel, Roo.grid);
54548         this.sm = this.selModel;
54549         this.sm.xmodule = this.xmodule || false;
54550     }
54551     if (typeof(this.colModel.config) == 'undefined') {
54552         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54553         this.cm = this.colModel;
54554         this.cm.xmodule = this.xmodule || false;
54555     }
54556     if (this.dataSource) {
54557         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54558         this.ds = this.dataSource;
54559         this.ds.xmodule = this.xmodule || false;
54560          
54561     }
54562     
54563     
54564     
54565     if(this.width){
54566         this.container.setWidth(this.width);
54567     }
54568
54569     if(this.height){
54570         this.container.setHeight(this.height);
54571     }
54572     /** @private */
54573         this.addEvents({
54574         // raw events
54575         /**
54576          * @event click
54577          * The raw click event for the entire grid.
54578          * @param {Roo.EventObject} e
54579          */
54580         "click" : true,
54581         /**
54582          * @event dblclick
54583          * The raw dblclick event for the entire grid.
54584          * @param {Roo.EventObject} e
54585          */
54586         "dblclick" : true,
54587         /**
54588          * @event contextmenu
54589          * The raw contextmenu event for the entire grid.
54590          * @param {Roo.EventObject} e
54591          */
54592         "contextmenu" : true,
54593         /**
54594          * @event mousedown
54595          * The raw mousedown event for the entire grid.
54596          * @param {Roo.EventObject} e
54597          */
54598         "mousedown" : true,
54599         /**
54600          * @event mouseup
54601          * The raw mouseup event for the entire grid.
54602          * @param {Roo.EventObject} e
54603          */
54604         "mouseup" : true,
54605         /**
54606          * @event mouseover
54607          * The raw mouseover event for the entire grid.
54608          * @param {Roo.EventObject} e
54609          */
54610         "mouseover" : true,
54611         /**
54612          * @event mouseout
54613          * The raw mouseout event for the entire grid.
54614          * @param {Roo.EventObject} e
54615          */
54616         "mouseout" : true,
54617         /**
54618          * @event keypress
54619          * The raw keypress event for the entire grid.
54620          * @param {Roo.EventObject} e
54621          */
54622         "keypress" : true,
54623         /**
54624          * @event keydown
54625          * The raw keydown event for the entire grid.
54626          * @param {Roo.EventObject} e
54627          */
54628         "keydown" : true,
54629
54630         // custom events
54631
54632         /**
54633          * @event cellclick
54634          * Fires when a cell is clicked
54635          * @param {Grid} this
54636          * @param {Number} rowIndex
54637          * @param {Number} columnIndex
54638          * @param {Roo.EventObject} e
54639          */
54640         "cellclick" : true,
54641         /**
54642          * @event celldblclick
54643          * Fires when a cell is double clicked
54644          * @param {Grid} this
54645          * @param {Number} rowIndex
54646          * @param {Number} columnIndex
54647          * @param {Roo.EventObject} e
54648          */
54649         "celldblclick" : true,
54650         /**
54651          * @event rowclick
54652          * Fires when a row is clicked
54653          * @param {Grid} this
54654          * @param {Number} rowIndex
54655          * @param {Roo.EventObject} e
54656          */
54657         "rowclick" : true,
54658         /**
54659          * @event rowdblclick
54660          * Fires when a row is double clicked
54661          * @param {Grid} this
54662          * @param {Number} rowIndex
54663          * @param {Roo.EventObject} e
54664          */
54665         "rowdblclick" : true,
54666         /**
54667          * @event headerclick
54668          * Fires when a header is clicked
54669          * @param {Grid} this
54670          * @param {Number} columnIndex
54671          * @param {Roo.EventObject} e
54672          */
54673         "headerclick" : true,
54674         /**
54675          * @event headerdblclick
54676          * Fires when a header cell is double clicked
54677          * @param {Grid} this
54678          * @param {Number} columnIndex
54679          * @param {Roo.EventObject} e
54680          */
54681         "headerdblclick" : true,
54682         /**
54683          * @event rowcontextmenu
54684          * Fires when a row is right clicked
54685          * @param {Grid} this
54686          * @param {Number} rowIndex
54687          * @param {Roo.EventObject} e
54688          */
54689         "rowcontextmenu" : true,
54690         /**
54691          * @event cellcontextmenu
54692          * Fires when a cell is right clicked
54693          * @param {Grid} this
54694          * @param {Number} rowIndex
54695          * @param {Number} cellIndex
54696          * @param {Roo.EventObject} e
54697          */
54698          "cellcontextmenu" : true,
54699         /**
54700          * @event headercontextmenu
54701          * Fires when a header is right clicked
54702          * @param {Grid} this
54703          * @param {Number} columnIndex
54704          * @param {Roo.EventObject} e
54705          */
54706         "headercontextmenu" : true,
54707         /**
54708          * @event bodyscroll
54709          * Fires when the body element is scrolled
54710          * @param {Number} scrollLeft
54711          * @param {Number} scrollTop
54712          */
54713         "bodyscroll" : true,
54714         /**
54715          * @event columnresize
54716          * Fires when the user resizes a column
54717          * @param {Number} columnIndex
54718          * @param {Number} newSize
54719          */
54720         "columnresize" : true,
54721         /**
54722          * @event columnmove
54723          * Fires when the user moves a column
54724          * @param {Number} oldIndex
54725          * @param {Number} newIndex
54726          */
54727         "columnmove" : true,
54728         /**
54729          * @event startdrag
54730          * Fires when row(s) start being dragged
54731          * @param {Grid} this
54732          * @param {Roo.GridDD} dd The drag drop object
54733          * @param {event} e The raw browser event
54734          */
54735         "startdrag" : true,
54736         /**
54737          * @event enddrag
54738          * Fires when a drag operation is complete
54739          * @param {Grid} this
54740          * @param {Roo.GridDD} dd The drag drop object
54741          * @param {event} e The raw browser event
54742          */
54743         "enddrag" : true,
54744         /**
54745          * @event dragdrop
54746          * Fires when dragged row(s) are dropped on a valid DD target
54747          * @param {Grid} this
54748          * @param {Roo.GridDD} dd The drag drop object
54749          * @param {String} targetId The target drag drop object
54750          * @param {event} e The raw browser event
54751          */
54752         "dragdrop" : true,
54753         /**
54754          * @event dragover
54755          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54756          * @param {Grid} this
54757          * @param {Roo.GridDD} dd The drag drop object
54758          * @param {String} targetId The target drag drop object
54759          * @param {event} e The raw browser event
54760          */
54761         "dragover" : true,
54762         /**
54763          * @event dragenter
54764          *  Fires when the dragged row(s) first cross another DD target while being dragged
54765          * @param {Grid} this
54766          * @param {Roo.GridDD} dd The drag drop object
54767          * @param {String} targetId The target drag drop object
54768          * @param {event} e The raw browser event
54769          */
54770         "dragenter" : true,
54771         /**
54772          * @event dragout
54773          * Fires when the dragged row(s) leave another DD target while being dragged
54774          * @param {Grid} this
54775          * @param {Roo.GridDD} dd The drag drop object
54776          * @param {String} targetId The target drag drop object
54777          * @param {event} e The raw browser event
54778          */
54779         "dragout" : true,
54780         /**
54781          * @event rowclass
54782          * Fires when a row is rendered, so you can change add a style to it.
54783          * @param {GridView} gridview   The grid view
54784          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54785          */
54786         'rowclass' : true,
54787
54788         /**
54789          * @event render
54790          * Fires when the grid is rendered
54791          * @param {Grid} grid
54792          */
54793         'render' : true
54794     });
54795
54796     Roo.grid.Grid.superclass.constructor.call(this);
54797 };
54798 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54799     
54800     /**
54801      * @cfg {String} ddGroup - drag drop group.
54802      */
54803
54804     /**
54805      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54806      */
54807     minColumnWidth : 25,
54808
54809     /**
54810      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54811      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54812      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54813      */
54814     autoSizeColumns : false,
54815
54816     /**
54817      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54818      */
54819     autoSizeHeaders : true,
54820
54821     /**
54822      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54823      */
54824     monitorWindowResize : true,
54825
54826     /**
54827      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54828      * rows measured to get a columns size. Default is 0 (all rows).
54829      */
54830     maxRowsToMeasure : 0,
54831
54832     /**
54833      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54834      */
54835     trackMouseOver : true,
54836
54837     /**
54838     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54839     */
54840     
54841     /**
54842     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54843     */
54844     enableDragDrop : false,
54845     
54846     /**
54847     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54848     */
54849     enableColumnMove : true,
54850     
54851     /**
54852     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54853     */
54854     enableColumnHide : true,
54855     
54856     /**
54857     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54858     */
54859     enableRowHeightSync : false,
54860     
54861     /**
54862     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54863     */
54864     stripeRows : true,
54865     
54866     /**
54867     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54868     */
54869     autoHeight : false,
54870
54871     /**
54872      * @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.
54873      */
54874     autoExpandColumn : false,
54875
54876     /**
54877     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54878     * Default is 50.
54879     */
54880     autoExpandMin : 50,
54881
54882     /**
54883     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54884     */
54885     autoExpandMax : 1000,
54886
54887     /**
54888     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54889     */
54890     view : null,
54891
54892     /**
54893     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54894     */
54895     loadMask : false,
54896     /**
54897     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54898     */
54899     dropTarget: false,
54900     
54901    
54902     
54903     // private
54904     rendered : false,
54905
54906     /**
54907     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54908     * of a fixed width. Default is false.
54909     */
54910     /**
54911     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54912     */
54913     /**
54914      * Called once after all setup has been completed and the grid is ready to be rendered.
54915      * @return {Roo.grid.Grid} this
54916      */
54917     render : function()
54918     {
54919         var c = this.container;
54920         // try to detect autoHeight/width mode
54921         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54922             this.autoHeight = true;
54923         }
54924         var view = this.getView();
54925         view.init(this);
54926
54927         c.on("click", this.onClick, this);
54928         c.on("dblclick", this.onDblClick, this);
54929         c.on("contextmenu", this.onContextMenu, this);
54930         c.on("keydown", this.onKeyDown, this);
54931         if (Roo.isTouch) {
54932             c.on("touchstart", this.onTouchStart, this);
54933         }
54934
54935         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54936
54937         this.getSelectionModel().init(this);
54938
54939         view.render();
54940
54941         if(this.loadMask){
54942             this.loadMask = new Roo.LoadMask(this.container,
54943                     Roo.apply({store:this.dataSource}, this.loadMask));
54944         }
54945         
54946         
54947         if (this.toolbar && this.toolbar.xtype) {
54948             this.toolbar.container = this.getView().getHeaderPanel(true);
54949             this.toolbar = new Roo.Toolbar(this.toolbar);
54950         }
54951         if (this.footer && this.footer.xtype) {
54952             this.footer.dataSource = this.getDataSource();
54953             this.footer.container = this.getView().getFooterPanel(true);
54954             this.footer = Roo.factory(this.footer, Roo);
54955         }
54956         if (this.dropTarget && this.dropTarget.xtype) {
54957             delete this.dropTarget.xtype;
54958             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54959         }
54960         
54961         
54962         this.rendered = true;
54963         this.fireEvent('render', this);
54964         return this;
54965     },
54966
54967         /**
54968          * Reconfigures the grid to use a different Store and Column Model.
54969          * The View will be bound to the new objects and refreshed.
54970          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54971          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54972          */
54973     reconfigure : function(dataSource, colModel){
54974         if(this.loadMask){
54975             this.loadMask.destroy();
54976             this.loadMask = new Roo.LoadMask(this.container,
54977                     Roo.apply({store:dataSource}, this.loadMask));
54978         }
54979         this.view.bind(dataSource, colModel);
54980         this.dataSource = dataSource;
54981         this.colModel = colModel;
54982         this.view.refresh(true);
54983     },
54984
54985     // private
54986     onKeyDown : function(e){
54987         this.fireEvent("keydown", e);
54988     },
54989
54990     /**
54991      * Destroy this grid.
54992      * @param {Boolean} removeEl True to remove the element
54993      */
54994     destroy : function(removeEl, keepListeners){
54995         if(this.loadMask){
54996             this.loadMask.destroy();
54997         }
54998         var c = this.container;
54999         c.removeAllListeners();
55000         this.view.destroy();
55001         this.colModel.purgeListeners();
55002         if(!keepListeners){
55003             this.purgeListeners();
55004         }
55005         c.update("");
55006         if(removeEl === true){
55007             c.remove();
55008         }
55009     },
55010
55011     // private
55012     processEvent : function(name, e){
55013         // does this fire select???
55014         //Roo.log('grid:processEvent '  + name);
55015         
55016         if (name != 'touchstart' ) {
55017             this.fireEvent(name, e);    
55018         }
55019         
55020         var t = e.getTarget();
55021         var v = this.view;
55022         var header = v.findHeaderIndex(t);
55023         if(header !== false){
55024             var ename = name == 'touchstart' ? 'click' : name;
55025              
55026             this.fireEvent("header" + ename, this, header, e);
55027         }else{
55028             var row = v.findRowIndex(t);
55029             var cell = v.findCellIndex(t);
55030             if (name == 'touchstart') {
55031                 // first touch is always a click.
55032                 // hopefull this happens after selection is updated.?
55033                 name = false;
55034                 
55035                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55036                     var cs = this.selModel.getSelectedCell();
55037                     if (row == cs[0] && cell == cs[1]){
55038                         name = 'dblclick';
55039                     }
55040                 }
55041                 if (typeof(this.selModel.getSelections) != 'undefined') {
55042                     var cs = this.selModel.getSelections();
55043                     var ds = this.dataSource;
55044                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55045                         name = 'dblclick';
55046                     }
55047                 }
55048                 if (!name) {
55049                     return;
55050                 }
55051             }
55052             
55053             
55054             if(row !== false){
55055                 this.fireEvent("row" + name, this, row, e);
55056                 if(cell !== false){
55057                     this.fireEvent("cell" + name, this, row, cell, e);
55058                 }
55059             }
55060         }
55061     },
55062
55063     // private
55064     onClick : function(e){
55065         this.processEvent("click", e);
55066     },
55067    // private
55068     onTouchStart : function(e){
55069         this.processEvent("touchstart", e);
55070     },
55071
55072     // private
55073     onContextMenu : function(e, t){
55074         this.processEvent("contextmenu", e);
55075     },
55076
55077     // private
55078     onDblClick : function(e){
55079         this.processEvent("dblclick", e);
55080     },
55081
55082     // private
55083     walkCells : function(row, col, step, fn, scope){
55084         var cm = this.colModel, clen = cm.getColumnCount();
55085         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55086         if(step < 0){
55087             if(col < 0){
55088                 row--;
55089                 first = false;
55090             }
55091             while(row >= 0){
55092                 if(!first){
55093                     col = clen-1;
55094                 }
55095                 first = false;
55096                 while(col >= 0){
55097                     if(fn.call(scope || this, row, col, cm) === true){
55098                         return [row, col];
55099                     }
55100                     col--;
55101                 }
55102                 row--;
55103             }
55104         } else {
55105             if(col >= clen){
55106                 row++;
55107                 first = false;
55108             }
55109             while(row < rlen){
55110                 if(!first){
55111                     col = 0;
55112                 }
55113                 first = false;
55114                 while(col < clen){
55115                     if(fn.call(scope || this, row, col, cm) === true){
55116                         return [row, col];
55117                     }
55118                     col++;
55119                 }
55120                 row++;
55121             }
55122         }
55123         return null;
55124     },
55125
55126     // private
55127     getSelections : function(){
55128         return this.selModel.getSelections();
55129     },
55130
55131     /**
55132      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55133      * but if manual update is required this method will initiate it.
55134      */
55135     autoSize : function(){
55136         if(this.rendered){
55137             this.view.layout();
55138             if(this.view.adjustForScroll){
55139                 this.view.adjustForScroll();
55140             }
55141         }
55142     },
55143
55144     /**
55145      * Returns the grid's underlying element.
55146      * @return {Element} The element
55147      */
55148     getGridEl : function(){
55149         return this.container;
55150     },
55151
55152     // private for compatibility, overridden by editor grid
55153     stopEditing : function(){},
55154
55155     /**
55156      * Returns the grid's SelectionModel.
55157      * @return {SelectionModel}
55158      */
55159     getSelectionModel : function(){
55160         if(!this.selModel){
55161             this.selModel = new Roo.grid.RowSelectionModel();
55162         }
55163         return this.selModel;
55164     },
55165
55166     /**
55167      * Returns the grid's DataSource.
55168      * @return {DataSource}
55169      */
55170     getDataSource : function(){
55171         return this.dataSource;
55172     },
55173
55174     /**
55175      * Returns the grid's ColumnModel.
55176      * @return {ColumnModel}
55177      */
55178     getColumnModel : function(){
55179         return this.colModel;
55180     },
55181
55182     /**
55183      * Returns the grid's GridView object.
55184      * @return {GridView}
55185      */
55186     getView : function(){
55187         if(!this.view){
55188             this.view = new Roo.grid.GridView(this.viewConfig);
55189         }
55190         return this.view;
55191     },
55192     /**
55193      * Called to get grid's drag proxy text, by default returns this.ddText.
55194      * @return {String}
55195      */
55196     getDragDropText : function(){
55197         var count = this.selModel.getCount();
55198         return String.format(this.ddText, count, count == 1 ? '' : 's');
55199     }
55200 });
55201 /**
55202  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55203  * %0 is replaced with the number of selected rows.
55204  * @type String
55205  */
55206 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55207  * Based on:
55208  * Ext JS Library 1.1.1
55209  * Copyright(c) 2006-2007, Ext JS, LLC.
55210  *
55211  * Originally Released Under LGPL - original licence link has changed is not relivant.
55212  *
55213  * Fork - LGPL
55214  * <script type="text/javascript">
55215  */
55216  
55217 Roo.grid.AbstractGridView = function(){
55218         this.grid = null;
55219         
55220         this.events = {
55221             "beforerowremoved" : true,
55222             "beforerowsinserted" : true,
55223             "beforerefresh" : true,
55224             "rowremoved" : true,
55225             "rowsinserted" : true,
55226             "rowupdated" : true,
55227             "refresh" : true
55228         };
55229     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55230 };
55231
55232 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55233     rowClass : "x-grid-row",
55234     cellClass : "x-grid-cell",
55235     tdClass : "x-grid-td",
55236     hdClass : "x-grid-hd",
55237     splitClass : "x-grid-hd-split",
55238     
55239     init: function(grid){
55240         this.grid = grid;
55241                 var cid = this.grid.getGridEl().id;
55242         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55243         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55244         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55245         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55246         },
55247         
55248     getColumnRenderers : function(){
55249         var renderers = [];
55250         var cm = this.grid.colModel;
55251         var colCount = cm.getColumnCount();
55252         for(var i = 0; i < colCount; i++){
55253             renderers[i] = cm.getRenderer(i);
55254         }
55255         return renderers;
55256     },
55257     
55258     getColumnIds : function(){
55259         var ids = [];
55260         var cm = this.grid.colModel;
55261         var colCount = cm.getColumnCount();
55262         for(var i = 0; i < colCount; i++){
55263             ids[i] = cm.getColumnId(i);
55264         }
55265         return ids;
55266     },
55267     
55268     getDataIndexes : function(){
55269         if(!this.indexMap){
55270             this.indexMap = this.buildIndexMap();
55271         }
55272         return this.indexMap.colToData;
55273     },
55274     
55275     getColumnIndexByDataIndex : function(dataIndex){
55276         if(!this.indexMap){
55277             this.indexMap = this.buildIndexMap();
55278         }
55279         return this.indexMap.dataToCol[dataIndex];
55280     },
55281     
55282     /**
55283      * Set a css style for a column dynamically. 
55284      * @param {Number} colIndex The index of the column
55285      * @param {String} name The css property name
55286      * @param {String} value The css value
55287      */
55288     setCSSStyle : function(colIndex, name, value){
55289         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55290         Roo.util.CSS.updateRule(selector, name, value);
55291     },
55292     
55293     generateRules : function(cm){
55294         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55295         Roo.util.CSS.removeStyleSheet(rulesId);
55296         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55297             var cid = cm.getColumnId(i);
55298             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55299                          this.tdSelector, cid, " {\n}\n",
55300                          this.hdSelector, cid, " {\n}\n",
55301                          this.splitSelector, cid, " {\n}\n");
55302         }
55303         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55304     }
55305 });/*
55306  * Based on:
55307  * Ext JS Library 1.1.1
55308  * Copyright(c) 2006-2007, Ext JS, LLC.
55309  *
55310  * Originally Released Under LGPL - original licence link has changed is not relivant.
55311  *
55312  * Fork - LGPL
55313  * <script type="text/javascript">
55314  */
55315
55316 // private
55317 // This is a support class used internally by the Grid components
55318 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55319     this.grid = grid;
55320     this.view = grid.getView();
55321     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55322     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55323     if(hd2){
55324         this.setHandleElId(Roo.id(hd));
55325         this.setOuterHandleElId(Roo.id(hd2));
55326     }
55327     this.scroll = false;
55328 };
55329 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55330     maxDragWidth: 120,
55331     getDragData : function(e){
55332         var t = Roo.lib.Event.getTarget(e);
55333         var h = this.view.findHeaderCell(t);
55334         if(h){
55335             return {ddel: h.firstChild, header:h};
55336         }
55337         return false;
55338     },
55339
55340     onInitDrag : function(e){
55341         this.view.headersDisabled = true;
55342         var clone = this.dragData.ddel.cloneNode(true);
55343         clone.id = Roo.id();
55344         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55345         this.proxy.update(clone);
55346         return true;
55347     },
55348
55349     afterValidDrop : function(){
55350         var v = this.view;
55351         setTimeout(function(){
55352             v.headersDisabled = false;
55353         }, 50);
55354     },
55355
55356     afterInvalidDrop : function(){
55357         var v = this.view;
55358         setTimeout(function(){
55359             v.headersDisabled = false;
55360         }, 50);
55361     }
55362 });
55363 /*
55364  * Based on:
55365  * Ext JS Library 1.1.1
55366  * Copyright(c) 2006-2007, Ext JS, LLC.
55367  *
55368  * Originally Released Under LGPL - original licence link has changed is not relivant.
55369  *
55370  * Fork - LGPL
55371  * <script type="text/javascript">
55372  */
55373 // private
55374 // This is a support class used internally by the Grid components
55375 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55376     this.grid = grid;
55377     this.view = grid.getView();
55378     // split the proxies so they don't interfere with mouse events
55379     this.proxyTop = Roo.DomHelper.append(document.body, {
55380         cls:"col-move-top", html:"&#160;"
55381     }, true);
55382     this.proxyBottom = Roo.DomHelper.append(document.body, {
55383         cls:"col-move-bottom", html:"&#160;"
55384     }, true);
55385     this.proxyTop.hide = this.proxyBottom.hide = function(){
55386         this.setLeftTop(-100,-100);
55387         this.setStyle("visibility", "hidden");
55388     };
55389     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55390     // temporarily disabled
55391     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55392     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55393 };
55394 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55395     proxyOffsets : [-4, -9],
55396     fly: Roo.Element.fly,
55397
55398     getTargetFromEvent : function(e){
55399         var t = Roo.lib.Event.getTarget(e);
55400         var cindex = this.view.findCellIndex(t);
55401         if(cindex !== false){
55402             return this.view.getHeaderCell(cindex);
55403         }
55404         return null;
55405     },
55406
55407     nextVisible : function(h){
55408         var v = this.view, cm = this.grid.colModel;
55409         h = h.nextSibling;
55410         while(h){
55411             if(!cm.isHidden(v.getCellIndex(h))){
55412                 return h;
55413             }
55414             h = h.nextSibling;
55415         }
55416         return null;
55417     },
55418
55419     prevVisible : function(h){
55420         var v = this.view, cm = this.grid.colModel;
55421         h = h.prevSibling;
55422         while(h){
55423             if(!cm.isHidden(v.getCellIndex(h))){
55424                 return h;
55425             }
55426             h = h.prevSibling;
55427         }
55428         return null;
55429     },
55430
55431     positionIndicator : function(h, n, e){
55432         var x = Roo.lib.Event.getPageX(e);
55433         var r = Roo.lib.Dom.getRegion(n.firstChild);
55434         var px, pt, py = r.top + this.proxyOffsets[1];
55435         if((r.right - x) <= (r.right-r.left)/2){
55436             px = r.right+this.view.borderWidth;
55437             pt = "after";
55438         }else{
55439             px = r.left;
55440             pt = "before";
55441         }
55442         var oldIndex = this.view.getCellIndex(h);
55443         var newIndex = this.view.getCellIndex(n);
55444
55445         if(this.grid.colModel.isFixed(newIndex)){
55446             return false;
55447         }
55448
55449         var locked = this.grid.colModel.isLocked(newIndex);
55450
55451         if(pt == "after"){
55452             newIndex++;
55453         }
55454         if(oldIndex < newIndex){
55455             newIndex--;
55456         }
55457         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55458             return false;
55459         }
55460         px +=  this.proxyOffsets[0];
55461         this.proxyTop.setLeftTop(px, py);
55462         this.proxyTop.show();
55463         if(!this.bottomOffset){
55464             this.bottomOffset = this.view.mainHd.getHeight();
55465         }
55466         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55467         this.proxyBottom.show();
55468         return pt;
55469     },
55470
55471     onNodeEnter : function(n, dd, e, data){
55472         if(data.header != n){
55473             this.positionIndicator(data.header, n, e);
55474         }
55475     },
55476
55477     onNodeOver : function(n, dd, e, data){
55478         var result = false;
55479         if(data.header != n){
55480             result = this.positionIndicator(data.header, n, e);
55481         }
55482         if(!result){
55483             this.proxyTop.hide();
55484             this.proxyBottom.hide();
55485         }
55486         return result ? this.dropAllowed : this.dropNotAllowed;
55487     },
55488
55489     onNodeOut : function(n, dd, e, data){
55490         this.proxyTop.hide();
55491         this.proxyBottom.hide();
55492     },
55493
55494     onNodeDrop : function(n, dd, e, data){
55495         var h = data.header;
55496         if(h != n){
55497             var cm = this.grid.colModel;
55498             var x = Roo.lib.Event.getPageX(e);
55499             var r = Roo.lib.Dom.getRegion(n.firstChild);
55500             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55501             var oldIndex = this.view.getCellIndex(h);
55502             var newIndex = this.view.getCellIndex(n);
55503             var locked = cm.isLocked(newIndex);
55504             if(pt == "after"){
55505                 newIndex++;
55506             }
55507             if(oldIndex < newIndex){
55508                 newIndex--;
55509             }
55510             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55511                 return false;
55512             }
55513             cm.setLocked(oldIndex, locked, true);
55514             cm.moveColumn(oldIndex, newIndex);
55515             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55516             return true;
55517         }
55518         return false;
55519     }
55520 });
55521 /*
55522  * Based on:
55523  * Ext JS Library 1.1.1
55524  * Copyright(c) 2006-2007, Ext JS, LLC.
55525  *
55526  * Originally Released Under LGPL - original licence link has changed is not relivant.
55527  *
55528  * Fork - LGPL
55529  * <script type="text/javascript">
55530  */
55531   
55532 /**
55533  * @class Roo.grid.GridView
55534  * @extends Roo.util.Observable
55535  *
55536  * @constructor
55537  * @param {Object} config
55538  */
55539 Roo.grid.GridView = function(config){
55540     Roo.grid.GridView.superclass.constructor.call(this);
55541     this.el = null;
55542
55543     Roo.apply(this, config);
55544 };
55545
55546 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55547
55548     unselectable :  'unselectable="on"',
55549     unselectableCls :  'x-unselectable',
55550     
55551     
55552     rowClass : "x-grid-row",
55553
55554     cellClass : "x-grid-col",
55555
55556     tdClass : "x-grid-td",
55557
55558     hdClass : "x-grid-hd",
55559
55560     splitClass : "x-grid-split",
55561
55562     sortClasses : ["sort-asc", "sort-desc"],
55563
55564     enableMoveAnim : false,
55565
55566     hlColor: "C3DAF9",
55567
55568     dh : Roo.DomHelper,
55569
55570     fly : Roo.Element.fly,
55571
55572     css : Roo.util.CSS,
55573
55574     borderWidth: 1,
55575
55576     splitOffset: 3,
55577
55578     scrollIncrement : 22,
55579
55580     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55581
55582     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55583
55584     bind : function(ds, cm){
55585         if(this.ds){
55586             this.ds.un("load", this.onLoad, this);
55587             this.ds.un("datachanged", this.onDataChange, this);
55588             this.ds.un("add", this.onAdd, this);
55589             this.ds.un("remove", this.onRemove, this);
55590             this.ds.un("update", this.onUpdate, this);
55591             this.ds.un("clear", this.onClear, this);
55592         }
55593         if(ds){
55594             ds.on("load", this.onLoad, this);
55595             ds.on("datachanged", this.onDataChange, this);
55596             ds.on("add", this.onAdd, this);
55597             ds.on("remove", this.onRemove, this);
55598             ds.on("update", this.onUpdate, this);
55599             ds.on("clear", this.onClear, this);
55600         }
55601         this.ds = ds;
55602
55603         if(this.cm){
55604             this.cm.un("widthchange", this.onColWidthChange, this);
55605             this.cm.un("headerchange", this.onHeaderChange, this);
55606             this.cm.un("hiddenchange", this.onHiddenChange, this);
55607             this.cm.un("columnmoved", this.onColumnMove, this);
55608             this.cm.un("columnlockchange", this.onColumnLock, this);
55609         }
55610         if(cm){
55611             this.generateRules(cm);
55612             cm.on("widthchange", this.onColWidthChange, this);
55613             cm.on("headerchange", this.onHeaderChange, this);
55614             cm.on("hiddenchange", this.onHiddenChange, this);
55615             cm.on("columnmoved", this.onColumnMove, this);
55616             cm.on("columnlockchange", this.onColumnLock, this);
55617         }
55618         this.cm = cm;
55619     },
55620
55621     init: function(grid){
55622         Roo.grid.GridView.superclass.init.call(this, grid);
55623
55624         this.bind(grid.dataSource, grid.colModel);
55625
55626         grid.on("headerclick", this.handleHeaderClick, this);
55627
55628         if(grid.trackMouseOver){
55629             grid.on("mouseover", this.onRowOver, this);
55630             grid.on("mouseout", this.onRowOut, this);
55631         }
55632         grid.cancelTextSelection = function(){};
55633         this.gridId = grid.id;
55634
55635         var tpls = this.templates || {};
55636
55637         if(!tpls.master){
55638             tpls.master = new Roo.Template(
55639                '<div class="x-grid" hidefocus="true">',
55640                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55641                   '<div class="x-grid-topbar"></div>',
55642                   '<div class="x-grid-scroller"><div></div></div>',
55643                   '<div class="x-grid-locked">',
55644                       '<div class="x-grid-header">{lockedHeader}</div>',
55645                       '<div class="x-grid-body">{lockedBody}</div>',
55646                   "</div>",
55647                   '<div class="x-grid-viewport">',
55648                       '<div class="x-grid-header">{header}</div>',
55649                       '<div class="x-grid-body">{body}</div>',
55650                   "</div>",
55651                   '<div class="x-grid-bottombar"></div>',
55652                  
55653                   '<div class="x-grid-resize-proxy">&#160;</div>',
55654                "</div>"
55655             );
55656             tpls.master.disableformats = true;
55657         }
55658
55659         if(!tpls.header){
55660             tpls.header = new Roo.Template(
55661                '<table border="0" cellspacing="0" cellpadding="0">',
55662                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55663                "</table>{splits}"
55664             );
55665             tpls.header.disableformats = true;
55666         }
55667         tpls.header.compile();
55668
55669         if(!tpls.hcell){
55670             tpls.hcell = new Roo.Template(
55671                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55672                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55673                 "</div></td>"
55674              );
55675              tpls.hcell.disableFormats = true;
55676         }
55677         tpls.hcell.compile();
55678
55679         if(!tpls.hsplit){
55680             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55681                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55682             tpls.hsplit.disableFormats = true;
55683         }
55684         tpls.hsplit.compile();
55685
55686         if(!tpls.body){
55687             tpls.body = new Roo.Template(
55688                '<table border="0" cellspacing="0" cellpadding="0">',
55689                "<tbody>{rows}</tbody>",
55690                "</table>"
55691             );
55692             tpls.body.disableFormats = true;
55693         }
55694         tpls.body.compile();
55695
55696         if(!tpls.row){
55697             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55698             tpls.row.disableFormats = true;
55699         }
55700         tpls.row.compile();
55701
55702         if(!tpls.cell){
55703             tpls.cell = new Roo.Template(
55704                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55705                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55706                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55707                 "</td>"
55708             );
55709             tpls.cell.disableFormats = true;
55710         }
55711         tpls.cell.compile();
55712
55713         this.templates = tpls;
55714     },
55715
55716     // remap these for backwards compat
55717     onColWidthChange : function(){
55718         this.updateColumns.apply(this, arguments);
55719     },
55720     onHeaderChange : function(){
55721         this.updateHeaders.apply(this, arguments);
55722     }, 
55723     onHiddenChange : function(){
55724         this.handleHiddenChange.apply(this, arguments);
55725     },
55726     onColumnMove : function(){
55727         this.handleColumnMove.apply(this, arguments);
55728     },
55729     onColumnLock : function(){
55730         this.handleLockChange.apply(this, arguments);
55731     },
55732
55733     onDataChange : function(){
55734         this.refresh();
55735         this.updateHeaderSortState();
55736     },
55737
55738     onClear : function(){
55739         this.refresh();
55740     },
55741
55742     onUpdate : function(ds, record){
55743         this.refreshRow(record);
55744     },
55745
55746     refreshRow : function(record){
55747         var ds = this.ds, index;
55748         if(typeof record == 'number'){
55749             index = record;
55750             record = ds.getAt(index);
55751         }else{
55752             index = ds.indexOf(record);
55753         }
55754         this.insertRows(ds, index, index, true);
55755         this.onRemove(ds, record, index+1, true);
55756         this.syncRowHeights(index, index);
55757         this.layout();
55758         this.fireEvent("rowupdated", this, index, record);
55759     },
55760
55761     onAdd : function(ds, records, index){
55762         this.insertRows(ds, index, index + (records.length-1));
55763     },
55764
55765     onRemove : function(ds, record, index, isUpdate){
55766         if(isUpdate !== true){
55767             this.fireEvent("beforerowremoved", this, index, record);
55768         }
55769         var bt = this.getBodyTable(), lt = this.getLockedTable();
55770         if(bt.rows[index]){
55771             bt.firstChild.removeChild(bt.rows[index]);
55772         }
55773         if(lt.rows[index]){
55774             lt.firstChild.removeChild(lt.rows[index]);
55775         }
55776         if(isUpdate !== true){
55777             this.stripeRows(index);
55778             this.syncRowHeights(index, index);
55779             this.layout();
55780             this.fireEvent("rowremoved", this, index, record);
55781         }
55782     },
55783
55784     onLoad : function(){
55785         this.scrollToTop();
55786     },
55787
55788     /**
55789      * Scrolls the grid to the top
55790      */
55791     scrollToTop : function(){
55792         if(this.scroller){
55793             this.scroller.dom.scrollTop = 0;
55794             this.syncScroll();
55795         }
55796     },
55797
55798     /**
55799      * Gets a panel in the header of the grid that can be used for toolbars etc.
55800      * After modifying the contents of this panel a call to grid.autoSize() may be
55801      * required to register any changes in size.
55802      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55803      * @return Roo.Element
55804      */
55805     getHeaderPanel : function(doShow){
55806         if(doShow){
55807             this.headerPanel.show();
55808         }
55809         return this.headerPanel;
55810     },
55811
55812     /**
55813      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55814      * After modifying the contents of this panel a call to grid.autoSize() may be
55815      * required to register any changes in size.
55816      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55817      * @return Roo.Element
55818      */
55819     getFooterPanel : function(doShow){
55820         if(doShow){
55821             this.footerPanel.show();
55822         }
55823         return this.footerPanel;
55824     },
55825
55826     initElements : function(){
55827         var E = Roo.Element;
55828         var el = this.grid.getGridEl().dom.firstChild;
55829         var cs = el.childNodes;
55830
55831         this.el = new E(el);
55832         
55833          this.focusEl = new E(el.firstChild);
55834         this.focusEl.swallowEvent("click", true);
55835         
55836         this.headerPanel = new E(cs[1]);
55837         this.headerPanel.enableDisplayMode("block");
55838
55839         this.scroller = new E(cs[2]);
55840         this.scrollSizer = new E(this.scroller.dom.firstChild);
55841
55842         this.lockedWrap = new E(cs[3]);
55843         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55844         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55845
55846         this.mainWrap = new E(cs[4]);
55847         this.mainHd = new E(this.mainWrap.dom.firstChild);
55848         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55849
55850         this.footerPanel = new E(cs[5]);
55851         this.footerPanel.enableDisplayMode("block");
55852
55853         this.resizeProxy = new E(cs[6]);
55854
55855         this.headerSelector = String.format(
55856            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55857            this.lockedHd.id, this.mainHd.id
55858         );
55859
55860         this.splitterSelector = String.format(
55861            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55862            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55863         );
55864     },
55865     idToCssName : function(s)
55866     {
55867         return s.replace(/[^a-z0-9]+/ig, '-');
55868     },
55869
55870     getHeaderCell : function(index){
55871         return Roo.DomQuery.select(this.headerSelector)[index];
55872     },
55873
55874     getHeaderCellMeasure : function(index){
55875         return this.getHeaderCell(index).firstChild;
55876     },
55877
55878     getHeaderCellText : function(index){
55879         return this.getHeaderCell(index).firstChild.firstChild;
55880     },
55881
55882     getLockedTable : function(){
55883         return this.lockedBody.dom.firstChild;
55884     },
55885
55886     getBodyTable : function(){
55887         return this.mainBody.dom.firstChild;
55888     },
55889
55890     getLockedRow : function(index){
55891         return this.getLockedTable().rows[index];
55892     },
55893
55894     getRow : function(index){
55895         return this.getBodyTable().rows[index];
55896     },
55897
55898     getRowComposite : function(index){
55899         if(!this.rowEl){
55900             this.rowEl = new Roo.CompositeElementLite();
55901         }
55902         var els = [], lrow, mrow;
55903         if(lrow = this.getLockedRow(index)){
55904             els.push(lrow);
55905         }
55906         if(mrow = this.getRow(index)){
55907             els.push(mrow);
55908         }
55909         this.rowEl.elements = els;
55910         return this.rowEl;
55911     },
55912     /**
55913      * Gets the 'td' of the cell
55914      * 
55915      * @param {Integer} rowIndex row to select
55916      * @param {Integer} colIndex column to select
55917      * 
55918      * @return {Object} 
55919      */
55920     getCell : function(rowIndex, colIndex){
55921         var locked = this.cm.getLockedCount();
55922         var source;
55923         if(colIndex < locked){
55924             source = this.lockedBody.dom.firstChild;
55925         }else{
55926             source = this.mainBody.dom.firstChild;
55927             colIndex -= locked;
55928         }
55929         return source.rows[rowIndex].childNodes[colIndex];
55930     },
55931
55932     getCellText : function(rowIndex, colIndex){
55933         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55934     },
55935
55936     getCellBox : function(cell){
55937         var b = this.fly(cell).getBox();
55938         if(Roo.isOpera){ // opera fails to report the Y
55939             b.y = cell.offsetTop + this.mainBody.getY();
55940         }
55941         return b;
55942     },
55943
55944     getCellIndex : function(cell){
55945         var id = String(cell.className).match(this.cellRE);
55946         if(id){
55947             return parseInt(id[1], 10);
55948         }
55949         return 0;
55950     },
55951
55952     findHeaderIndex : function(n){
55953         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55954         return r ? this.getCellIndex(r) : false;
55955     },
55956
55957     findHeaderCell : function(n){
55958         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55959         return r ? r : false;
55960     },
55961
55962     findRowIndex : function(n){
55963         if(!n){
55964             return false;
55965         }
55966         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55967         return r ? r.rowIndex : false;
55968     },
55969
55970     findCellIndex : function(node){
55971         var stop = this.el.dom;
55972         while(node && node != stop){
55973             if(this.findRE.test(node.className)){
55974                 return this.getCellIndex(node);
55975             }
55976             node = node.parentNode;
55977         }
55978         return false;
55979     },
55980
55981     getColumnId : function(index){
55982         return this.cm.getColumnId(index);
55983     },
55984
55985     getSplitters : function()
55986     {
55987         if(this.splitterSelector){
55988            return Roo.DomQuery.select(this.splitterSelector);
55989         }else{
55990             return null;
55991       }
55992     },
55993
55994     getSplitter : function(index){
55995         return this.getSplitters()[index];
55996     },
55997
55998     onRowOver : function(e, t){
55999         var row;
56000         if((row = this.findRowIndex(t)) !== false){
56001             this.getRowComposite(row).addClass("x-grid-row-over");
56002         }
56003     },
56004
56005     onRowOut : function(e, t){
56006         var row;
56007         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56008             this.getRowComposite(row).removeClass("x-grid-row-over");
56009         }
56010     },
56011
56012     renderHeaders : function(){
56013         var cm = this.cm;
56014         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56015         var cb = [], lb = [], sb = [], lsb = [], p = {};
56016         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56017             p.cellId = "x-grid-hd-0-" + i;
56018             p.splitId = "x-grid-csplit-0-" + i;
56019             p.id = cm.getColumnId(i);
56020             p.value = cm.getColumnHeader(i) || "";
56021             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56022             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56023             if(!cm.isLocked(i)){
56024                 cb[cb.length] = ct.apply(p);
56025                 sb[sb.length] = st.apply(p);
56026             }else{
56027                 lb[lb.length] = ct.apply(p);
56028                 lsb[lsb.length] = st.apply(p);
56029             }
56030         }
56031         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56032                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56033     },
56034
56035     updateHeaders : function(){
56036         var html = this.renderHeaders();
56037         this.lockedHd.update(html[0]);
56038         this.mainHd.update(html[1]);
56039     },
56040
56041     /**
56042      * Focuses the specified row.
56043      * @param {Number} row The row index
56044      */
56045     focusRow : function(row)
56046     {
56047         //Roo.log('GridView.focusRow');
56048         var x = this.scroller.dom.scrollLeft;
56049         this.focusCell(row, 0, false);
56050         this.scroller.dom.scrollLeft = x;
56051     },
56052
56053     /**
56054      * Focuses the specified cell.
56055      * @param {Number} row The row index
56056      * @param {Number} col The column index
56057      * @param {Boolean} hscroll false to disable horizontal scrolling
56058      */
56059     focusCell : function(row, col, hscroll)
56060     {
56061         //Roo.log('GridView.focusCell');
56062         var el = this.ensureVisible(row, col, hscroll);
56063         this.focusEl.alignTo(el, "tl-tl");
56064         if(Roo.isGecko){
56065             this.focusEl.focus();
56066         }else{
56067             this.focusEl.focus.defer(1, this.focusEl);
56068         }
56069     },
56070
56071     /**
56072      * Scrolls the specified cell into view
56073      * @param {Number} row The row index
56074      * @param {Number} col The column index
56075      * @param {Boolean} hscroll false to disable horizontal scrolling
56076      */
56077     ensureVisible : function(row, col, hscroll)
56078     {
56079         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56080         //return null; //disable for testing.
56081         if(typeof row != "number"){
56082             row = row.rowIndex;
56083         }
56084         if(row < 0 && row >= this.ds.getCount()){
56085             return  null;
56086         }
56087         col = (col !== undefined ? col : 0);
56088         var cm = this.grid.colModel;
56089         while(cm.isHidden(col)){
56090             col++;
56091         }
56092
56093         var el = this.getCell(row, col);
56094         if(!el){
56095             return null;
56096         }
56097         var c = this.scroller.dom;
56098
56099         var ctop = parseInt(el.offsetTop, 10);
56100         var cleft = parseInt(el.offsetLeft, 10);
56101         var cbot = ctop + el.offsetHeight;
56102         var cright = cleft + el.offsetWidth;
56103         
56104         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56105         var stop = parseInt(c.scrollTop, 10);
56106         var sleft = parseInt(c.scrollLeft, 10);
56107         var sbot = stop + ch;
56108         var sright = sleft + c.clientWidth;
56109         /*
56110         Roo.log('GridView.ensureVisible:' +
56111                 ' ctop:' + ctop +
56112                 ' c.clientHeight:' + c.clientHeight +
56113                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56114                 ' stop:' + stop +
56115                 ' cbot:' + cbot +
56116                 ' sbot:' + sbot +
56117                 ' ch:' + ch  
56118                 );
56119         */
56120         if(ctop < stop){
56121              c.scrollTop = ctop;
56122             //Roo.log("set scrolltop to ctop DISABLE?");
56123         }else if(cbot > sbot){
56124             //Roo.log("set scrolltop to cbot-ch");
56125             c.scrollTop = cbot-ch;
56126         }
56127         
56128         if(hscroll !== false){
56129             if(cleft < sleft){
56130                 c.scrollLeft = cleft;
56131             }else if(cright > sright){
56132                 c.scrollLeft = cright-c.clientWidth;
56133             }
56134         }
56135          
56136         return el;
56137     },
56138
56139     updateColumns : function(){
56140         this.grid.stopEditing();
56141         var cm = this.grid.colModel, colIds = this.getColumnIds();
56142         //var totalWidth = cm.getTotalWidth();
56143         var pos = 0;
56144         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56145             //if(cm.isHidden(i)) continue;
56146             var w = cm.getColumnWidth(i);
56147             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56148             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56149         }
56150         this.updateSplitters();
56151     },
56152
56153     generateRules : function(cm){
56154         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56155         Roo.util.CSS.removeStyleSheet(rulesId);
56156         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56157             var cid = cm.getColumnId(i);
56158             var align = '';
56159             if(cm.config[i].align){
56160                 align = 'text-align:'+cm.config[i].align+';';
56161             }
56162             var hidden = '';
56163             if(cm.isHidden(i)){
56164                 hidden = 'display:none;';
56165             }
56166             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56167             ruleBuf.push(
56168                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56169                     this.hdSelector, cid, " {\n", align, width, "}\n",
56170                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56171                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56172         }
56173         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56174     },
56175
56176     updateSplitters : function(){
56177         var cm = this.cm, s = this.getSplitters();
56178         if(s){ // splitters not created yet
56179             var pos = 0, locked = true;
56180             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56181                 if(cm.isHidden(i)) {
56182                     continue;
56183                 }
56184                 var w = cm.getColumnWidth(i); // make sure it's a number
56185                 if(!cm.isLocked(i) && locked){
56186                     pos = 0;
56187                     locked = false;
56188                 }
56189                 pos += w;
56190                 s[i].style.left = (pos-this.splitOffset) + "px";
56191             }
56192         }
56193     },
56194
56195     handleHiddenChange : function(colModel, colIndex, hidden){
56196         if(hidden){
56197             this.hideColumn(colIndex);
56198         }else{
56199             this.unhideColumn(colIndex);
56200         }
56201     },
56202
56203     hideColumn : function(colIndex){
56204         var cid = this.getColumnId(colIndex);
56205         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56206         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56207         if(Roo.isSafari){
56208             this.updateHeaders();
56209         }
56210         this.updateSplitters();
56211         this.layout();
56212     },
56213
56214     unhideColumn : function(colIndex){
56215         var cid = this.getColumnId(colIndex);
56216         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56217         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56218
56219         if(Roo.isSafari){
56220             this.updateHeaders();
56221         }
56222         this.updateSplitters();
56223         this.layout();
56224     },
56225
56226     insertRows : function(dm, firstRow, lastRow, isUpdate){
56227         if(firstRow == 0 && lastRow == dm.getCount()-1){
56228             this.refresh();
56229         }else{
56230             if(!isUpdate){
56231                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56232             }
56233             var s = this.getScrollState();
56234             var markup = this.renderRows(firstRow, lastRow);
56235             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56236             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56237             this.restoreScroll(s);
56238             if(!isUpdate){
56239                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56240                 this.syncRowHeights(firstRow, lastRow);
56241                 this.stripeRows(firstRow);
56242                 this.layout();
56243             }
56244         }
56245     },
56246
56247     bufferRows : function(markup, target, index){
56248         var before = null, trows = target.rows, tbody = target.tBodies[0];
56249         if(index < trows.length){
56250             before = trows[index];
56251         }
56252         var b = document.createElement("div");
56253         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56254         var rows = b.firstChild.rows;
56255         for(var i = 0, len = rows.length; i < len; i++){
56256             if(before){
56257                 tbody.insertBefore(rows[0], before);
56258             }else{
56259                 tbody.appendChild(rows[0]);
56260             }
56261         }
56262         b.innerHTML = "";
56263         b = null;
56264     },
56265
56266     deleteRows : function(dm, firstRow, lastRow){
56267         if(dm.getRowCount()<1){
56268             this.fireEvent("beforerefresh", this);
56269             this.mainBody.update("");
56270             this.lockedBody.update("");
56271             this.fireEvent("refresh", this);
56272         }else{
56273             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56274             var bt = this.getBodyTable();
56275             var tbody = bt.firstChild;
56276             var rows = bt.rows;
56277             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56278                 tbody.removeChild(rows[firstRow]);
56279             }
56280             this.stripeRows(firstRow);
56281             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56282         }
56283     },
56284
56285     updateRows : function(dataSource, firstRow, lastRow){
56286         var s = this.getScrollState();
56287         this.refresh();
56288         this.restoreScroll(s);
56289     },
56290
56291     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56292         if(!noRefresh){
56293            this.refresh();
56294         }
56295         this.updateHeaderSortState();
56296     },
56297
56298     getScrollState : function(){
56299         
56300         var sb = this.scroller.dom;
56301         return {left: sb.scrollLeft, top: sb.scrollTop};
56302     },
56303
56304     stripeRows : function(startRow){
56305         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56306             return;
56307         }
56308         startRow = startRow || 0;
56309         var rows = this.getBodyTable().rows;
56310         var lrows = this.getLockedTable().rows;
56311         var cls = ' x-grid-row-alt ';
56312         for(var i = startRow, len = rows.length; i < len; i++){
56313             var row = rows[i], lrow = lrows[i];
56314             var isAlt = ((i+1) % 2 == 0);
56315             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56316             if(isAlt == hasAlt){
56317                 continue;
56318             }
56319             if(isAlt){
56320                 row.className += " x-grid-row-alt";
56321             }else{
56322                 row.className = row.className.replace("x-grid-row-alt", "");
56323             }
56324             if(lrow){
56325                 lrow.className = row.className;
56326             }
56327         }
56328     },
56329
56330     restoreScroll : function(state){
56331         //Roo.log('GridView.restoreScroll');
56332         var sb = this.scroller.dom;
56333         sb.scrollLeft = state.left;
56334         sb.scrollTop = state.top;
56335         this.syncScroll();
56336     },
56337
56338     syncScroll : function(){
56339         //Roo.log('GridView.syncScroll');
56340         var sb = this.scroller.dom;
56341         var sh = this.mainHd.dom;
56342         var bs = this.mainBody.dom;
56343         var lv = this.lockedBody.dom;
56344         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56345         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56346     },
56347
56348     handleScroll : function(e){
56349         this.syncScroll();
56350         var sb = this.scroller.dom;
56351         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56352         e.stopEvent();
56353     },
56354
56355     handleWheel : function(e){
56356         var d = e.getWheelDelta();
56357         this.scroller.dom.scrollTop -= d*22;
56358         // set this here to prevent jumpy scrolling on large tables
56359         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56360         e.stopEvent();
56361     },
56362
56363     renderRows : function(startRow, endRow){
56364         // pull in all the crap needed to render rows
56365         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56366         var colCount = cm.getColumnCount();
56367
56368         if(ds.getCount() < 1){
56369             return ["", ""];
56370         }
56371
56372         // build a map for all the columns
56373         var cs = [];
56374         for(var i = 0; i < colCount; i++){
56375             var name = cm.getDataIndex(i);
56376             cs[i] = {
56377                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56378                 renderer : cm.getRenderer(i),
56379                 id : cm.getColumnId(i),
56380                 locked : cm.isLocked(i),
56381                 has_editor : cm.isCellEditable(i)
56382             };
56383         }
56384
56385         startRow = startRow || 0;
56386         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56387
56388         // records to render
56389         var rs = ds.getRange(startRow, endRow);
56390
56391         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56392     },
56393
56394     // As much as I hate to duplicate code, this was branched because FireFox really hates
56395     // [].join("") on strings. The performance difference was substantial enough to
56396     // branch this function
56397     doRender : Roo.isGecko ?
56398             function(cs, rs, ds, startRow, colCount, stripe){
56399                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56400                 // buffers
56401                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56402                 
56403                 var hasListener = this.grid.hasListener('rowclass');
56404                 var rowcfg = {};
56405                 for(var j = 0, len = rs.length; j < len; j++){
56406                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56407                     for(var i = 0; i < colCount; i++){
56408                         c = cs[i];
56409                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56410                         p.id = c.id;
56411                         p.css = p.attr = "";
56412                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56413                         if(p.value == undefined || p.value === "") {
56414                             p.value = "&#160;";
56415                         }
56416                         if(c.has_editor){
56417                             p.css += ' x-grid-editable-cell';
56418                         }
56419                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56420                             p.css +=  ' x-grid-dirty-cell';
56421                         }
56422                         var markup = ct.apply(p);
56423                         if(!c.locked){
56424                             cb+= markup;
56425                         }else{
56426                             lcb+= markup;
56427                         }
56428                     }
56429                     var alt = [];
56430                     if(stripe && ((rowIndex+1) % 2 == 0)){
56431                         alt.push("x-grid-row-alt")
56432                     }
56433                     if(r.dirty){
56434                         alt.push(  " x-grid-dirty-row");
56435                     }
56436                     rp.cells = lcb;
56437                     if(this.getRowClass){
56438                         alt.push(this.getRowClass(r, rowIndex));
56439                     }
56440                     if (hasListener) {
56441                         rowcfg = {
56442                              
56443                             record: r,
56444                             rowIndex : rowIndex,
56445                             rowClass : ''
56446                         };
56447                         this.grid.fireEvent('rowclass', this, rowcfg);
56448                         alt.push(rowcfg.rowClass);
56449                     }
56450                     rp.alt = alt.join(" ");
56451                     lbuf+= rt.apply(rp);
56452                     rp.cells = cb;
56453                     buf+=  rt.apply(rp);
56454                 }
56455                 return [lbuf, buf];
56456             } :
56457             function(cs, rs, ds, startRow, colCount, stripe){
56458                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56459                 // buffers
56460                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56461                 var hasListener = this.grid.hasListener('rowclass');
56462  
56463                 var rowcfg = {};
56464                 for(var j = 0, len = rs.length; j < len; j++){
56465                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56466                     for(var i = 0; i < colCount; i++){
56467                         c = cs[i];
56468                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56469                         p.id = c.id;
56470                         p.css = p.attr = "";
56471                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56472                         if(p.value == undefined || p.value === "") {
56473                             p.value = "&#160;";
56474                         }
56475                         //Roo.log(c);
56476                          if(c.has_editor){
56477                             p.css += ' x-grid-editable-cell';
56478                         }
56479                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56480                             p.css += ' x-grid-dirty-cell' 
56481                         }
56482                         
56483                         var markup = ct.apply(p);
56484                         if(!c.locked){
56485                             cb[cb.length] = markup;
56486                         }else{
56487                             lcb[lcb.length] = markup;
56488                         }
56489                     }
56490                     var alt = [];
56491                     if(stripe && ((rowIndex+1) % 2 == 0)){
56492                         alt.push( "x-grid-row-alt");
56493                     }
56494                     if(r.dirty){
56495                         alt.push(" x-grid-dirty-row");
56496                     }
56497                     rp.cells = lcb;
56498                     if(this.getRowClass){
56499                         alt.push( this.getRowClass(r, rowIndex));
56500                     }
56501                     if (hasListener) {
56502                         rowcfg = {
56503                              
56504                             record: r,
56505                             rowIndex : rowIndex,
56506                             rowClass : ''
56507                         };
56508                         this.grid.fireEvent('rowclass', this, rowcfg);
56509                         alt.push(rowcfg.rowClass);
56510                     }
56511                     
56512                     rp.alt = alt.join(" ");
56513                     rp.cells = lcb.join("");
56514                     lbuf[lbuf.length] = rt.apply(rp);
56515                     rp.cells = cb.join("");
56516                     buf[buf.length] =  rt.apply(rp);
56517                 }
56518                 return [lbuf.join(""), buf.join("")];
56519             },
56520
56521     renderBody : function(){
56522         var markup = this.renderRows();
56523         var bt = this.templates.body;
56524         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56525     },
56526
56527     /**
56528      * Refreshes the grid
56529      * @param {Boolean} headersToo
56530      */
56531     refresh : function(headersToo){
56532         this.fireEvent("beforerefresh", this);
56533         this.grid.stopEditing();
56534         var result = this.renderBody();
56535         this.lockedBody.update(result[0]);
56536         this.mainBody.update(result[1]);
56537         if(headersToo === true){
56538             this.updateHeaders();
56539             this.updateColumns();
56540             this.updateSplitters();
56541             this.updateHeaderSortState();
56542         }
56543         this.syncRowHeights();
56544         this.layout();
56545         this.fireEvent("refresh", this);
56546     },
56547
56548     handleColumnMove : function(cm, oldIndex, newIndex){
56549         this.indexMap = null;
56550         var s = this.getScrollState();
56551         this.refresh(true);
56552         this.restoreScroll(s);
56553         this.afterMove(newIndex);
56554     },
56555
56556     afterMove : function(colIndex){
56557         if(this.enableMoveAnim && Roo.enableFx){
56558             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56559         }
56560         // if multisort - fix sortOrder, and reload..
56561         if (this.grid.dataSource.multiSort) {
56562             // the we can call sort again..
56563             var dm = this.grid.dataSource;
56564             var cm = this.grid.colModel;
56565             var so = [];
56566             for(var i = 0; i < cm.config.length; i++ ) {
56567                 
56568                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56569                     continue; // dont' bother, it's not in sort list or being set.
56570                 }
56571                 
56572                 so.push(cm.config[i].dataIndex);
56573             };
56574             dm.sortOrder = so;
56575             dm.load(dm.lastOptions);
56576             
56577             
56578         }
56579         
56580     },
56581
56582     updateCell : function(dm, rowIndex, dataIndex){
56583         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56584         if(typeof colIndex == "undefined"){ // not present in grid
56585             return;
56586         }
56587         var cm = this.grid.colModel;
56588         var cell = this.getCell(rowIndex, colIndex);
56589         var cellText = this.getCellText(rowIndex, colIndex);
56590
56591         var p = {
56592             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56593             id : cm.getColumnId(colIndex),
56594             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56595         };
56596         var renderer = cm.getRenderer(colIndex);
56597         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56598         if(typeof val == "undefined" || val === "") {
56599             val = "&#160;";
56600         }
56601         cellText.innerHTML = val;
56602         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56603         this.syncRowHeights(rowIndex, rowIndex);
56604     },
56605
56606     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56607         var maxWidth = 0;
56608         if(this.grid.autoSizeHeaders){
56609             var h = this.getHeaderCellMeasure(colIndex);
56610             maxWidth = Math.max(maxWidth, h.scrollWidth);
56611         }
56612         var tb, index;
56613         if(this.cm.isLocked(colIndex)){
56614             tb = this.getLockedTable();
56615             index = colIndex;
56616         }else{
56617             tb = this.getBodyTable();
56618             index = colIndex - this.cm.getLockedCount();
56619         }
56620         if(tb && tb.rows){
56621             var rows = tb.rows;
56622             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56623             for(var i = 0; i < stopIndex; i++){
56624                 var cell = rows[i].childNodes[index].firstChild;
56625                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56626             }
56627         }
56628         return maxWidth + /*margin for error in IE*/ 5;
56629     },
56630     /**
56631      * Autofit a column to its content.
56632      * @param {Number} colIndex
56633      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56634      */
56635      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56636          if(this.cm.isHidden(colIndex)){
56637              return; // can't calc a hidden column
56638          }
56639         if(forceMinSize){
56640             var cid = this.cm.getColumnId(colIndex);
56641             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56642            if(this.grid.autoSizeHeaders){
56643                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56644            }
56645         }
56646         var newWidth = this.calcColumnWidth(colIndex);
56647         this.cm.setColumnWidth(colIndex,
56648             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56649         if(!suppressEvent){
56650             this.grid.fireEvent("columnresize", colIndex, newWidth);
56651         }
56652     },
56653
56654     /**
56655      * Autofits all columns to their content and then expands to fit any extra space in the grid
56656      */
56657      autoSizeColumns : function(){
56658         var cm = this.grid.colModel;
56659         var colCount = cm.getColumnCount();
56660         for(var i = 0; i < colCount; i++){
56661             this.autoSizeColumn(i, true, true);
56662         }
56663         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56664             this.fitColumns();
56665         }else{
56666             this.updateColumns();
56667             this.layout();
56668         }
56669     },
56670
56671     /**
56672      * Autofits all columns to the grid's width proportionate with their current size
56673      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56674      */
56675     fitColumns : function(reserveScrollSpace){
56676         var cm = this.grid.colModel;
56677         var colCount = cm.getColumnCount();
56678         var cols = [];
56679         var width = 0;
56680         var i, w;
56681         for (i = 0; i < colCount; i++){
56682             if(!cm.isHidden(i) && !cm.isFixed(i)){
56683                 w = cm.getColumnWidth(i);
56684                 cols.push(i);
56685                 cols.push(w);
56686                 width += w;
56687             }
56688         }
56689         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56690         if(reserveScrollSpace){
56691             avail -= 17;
56692         }
56693         var frac = (avail - cm.getTotalWidth())/width;
56694         while (cols.length){
56695             w = cols.pop();
56696             i = cols.pop();
56697             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56698         }
56699         this.updateColumns();
56700         this.layout();
56701     },
56702
56703     onRowSelect : function(rowIndex){
56704         var row = this.getRowComposite(rowIndex);
56705         row.addClass("x-grid-row-selected");
56706     },
56707
56708     onRowDeselect : function(rowIndex){
56709         var row = this.getRowComposite(rowIndex);
56710         row.removeClass("x-grid-row-selected");
56711     },
56712
56713     onCellSelect : function(row, col){
56714         var cell = this.getCell(row, col);
56715         if(cell){
56716             Roo.fly(cell).addClass("x-grid-cell-selected");
56717         }
56718     },
56719
56720     onCellDeselect : function(row, col){
56721         var cell = this.getCell(row, col);
56722         if(cell){
56723             Roo.fly(cell).removeClass("x-grid-cell-selected");
56724         }
56725     },
56726
56727     updateHeaderSortState : function(){
56728         
56729         // sort state can be single { field: xxx, direction : yyy}
56730         // or   { xxx=>ASC , yyy : DESC ..... }
56731         
56732         var mstate = {};
56733         if (!this.ds.multiSort) { 
56734             var state = this.ds.getSortState();
56735             if(!state){
56736                 return;
56737             }
56738             mstate[state.field] = state.direction;
56739             // FIXME... - this is not used here.. but might be elsewhere..
56740             this.sortState = state;
56741             
56742         } else {
56743             mstate = this.ds.sortToggle;
56744         }
56745         //remove existing sort classes..
56746         
56747         var sc = this.sortClasses;
56748         var hds = this.el.select(this.headerSelector).removeClass(sc);
56749         
56750         for(var f in mstate) {
56751         
56752             var sortColumn = this.cm.findColumnIndex(f);
56753             
56754             if(sortColumn != -1){
56755                 var sortDir = mstate[f];        
56756                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56757             }
56758         }
56759         
56760          
56761         
56762     },
56763
56764
56765     handleHeaderClick : function(g, index,e){
56766         
56767         Roo.log("header click");
56768         
56769         if (Roo.isTouch) {
56770             // touch events on header are handled by context
56771             this.handleHdCtx(g,index,e);
56772             return;
56773         }
56774         
56775         
56776         if(this.headersDisabled){
56777             return;
56778         }
56779         var dm = g.dataSource, cm = g.colModel;
56780         if(!cm.isSortable(index)){
56781             return;
56782         }
56783         g.stopEditing();
56784         
56785         if (dm.multiSort) {
56786             // update the sortOrder
56787             var so = [];
56788             for(var i = 0; i < cm.config.length; i++ ) {
56789                 
56790                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56791                     continue; // dont' bother, it's not in sort list or being set.
56792                 }
56793                 
56794                 so.push(cm.config[i].dataIndex);
56795             };
56796             dm.sortOrder = so;
56797         }
56798         
56799         
56800         dm.sort(cm.getDataIndex(index));
56801     },
56802
56803
56804     destroy : function(){
56805         if(this.colMenu){
56806             this.colMenu.removeAll();
56807             Roo.menu.MenuMgr.unregister(this.colMenu);
56808             this.colMenu.getEl().remove();
56809             delete this.colMenu;
56810         }
56811         if(this.hmenu){
56812             this.hmenu.removeAll();
56813             Roo.menu.MenuMgr.unregister(this.hmenu);
56814             this.hmenu.getEl().remove();
56815             delete this.hmenu;
56816         }
56817         if(this.grid.enableColumnMove){
56818             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56819             if(dds){
56820                 for(var dd in dds){
56821                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56822                         var elid = dds[dd].dragElId;
56823                         dds[dd].unreg();
56824                         Roo.get(elid).remove();
56825                     } else if(dds[dd].config.isTarget){
56826                         dds[dd].proxyTop.remove();
56827                         dds[dd].proxyBottom.remove();
56828                         dds[dd].unreg();
56829                     }
56830                     if(Roo.dd.DDM.locationCache[dd]){
56831                         delete Roo.dd.DDM.locationCache[dd];
56832                     }
56833                 }
56834                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56835             }
56836         }
56837         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56838         this.bind(null, null);
56839         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56840     },
56841
56842     handleLockChange : function(){
56843         this.refresh(true);
56844     },
56845
56846     onDenyColumnLock : function(){
56847
56848     },
56849
56850     onDenyColumnHide : function(){
56851
56852     },
56853
56854     handleHdMenuClick : function(item){
56855         var index = this.hdCtxIndex;
56856         var cm = this.cm, ds = this.ds;
56857         switch(item.id){
56858             case "asc":
56859                 ds.sort(cm.getDataIndex(index), "ASC");
56860                 break;
56861             case "desc":
56862                 ds.sort(cm.getDataIndex(index), "DESC");
56863                 break;
56864             case "lock":
56865                 var lc = cm.getLockedCount();
56866                 if(cm.getColumnCount(true) <= lc+1){
56867                     this.onDenyColumnLock();
56868                     return;
56869                 }
56870                 if(lc != index){
56871                     cm.setLocked(index, true, true);
56872                     cm.moveColumn(index, lc);
56873                     this.grid.fireEvent("columnmove", index, lc);
56874                 }else{
56875                     cm.setLocked(index, true);
56876                 }
56877             break;
56878             case "unlock":
56879                 var lc = cm.getLockedCount();
56880                 if((lc-1) != index){
56881                     cm.setLocked(index, false, true);
56882                     cm.moveColumn(index, lc-1);
56883                     this.grid.fireEvent("columnmove", index, lc-1);
56884                 }else{
56885                     cm.setLocked(index, false);
56886                 }
56887             break;
56888             case 'wider': // used to expand cols on touch..
56889             case 'narrow':
56890                 var cw = cm.getColumnWidth(index);
56891                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56892                 cw = Math.max(0, cw);
56893                 cw = Math.min(cw,4000);
56894                 cm.setColumnWidth(index, cw);
56895                 break;
56896                 
56897             default:
56898                 index = cm.getIndexById(item.id.substr(4));
56899                 if(index != -1){
56900                     if(item.checked && cm.getColumnCount(true) <= 1){
56901                         this.onDenyColumnHide();
56902                         return false;
56903                     }
56904                     cm.setHidden(index, item.checked);
56905                 }
56906         }
56907         return true;
56908     },
56909
56910     beforeColMenuShow : function(){
56911         var cm = this.cm,  colCount = cm.getColumnCount();
56912         this.colMenu.removeAll();
56913         for(var i = 0; i < colCount; i++){
56914             this.colMenu.add(new Roo.menu.CheckItem({
56915                 id: "col-"+cm.getColumnId(i),
56916                 text: cm.getColumnHeader(i),
56917                 checked: !cm.isHidden(i),
56918                 hideOnClick:false
56919             }));
56920         }
56921     },
56922
56923     handleHdCtx : function(g, index, e){
56924         e.stopEvent();
56925         var hd = this.getHeaderCell(index);
56926         this.hdCtxIndex = index;
56927         var ms = this.hmenu.items, cm = this.cm;
56928         ms.get("asc").setDisabled(!cm.isSortable(index));
56929         ms.get("desc").setDisabled(!cm.isSortable(index));
56930         if(this.grid.enableColLock !== false){
56931             ms.get("lock").setDisabled(cm.isLocked(index));
56932             ms.get("unlock").setDisabled(!cm.isLocked(index));
56933         }
56934         this.hmenu.show(hd, "tl-bl");
56935     },
56936
56937     handleHdOver : function(e){
56938         var hd = this.findHeaderCell(e.getTarget());
56939         if(hd && !this.headersDisabled){
56940             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56941                this.fly(hd).addClass("x-grid-hd-over");
56942             }
56943         }
56944     },
56945
56946     handleHdOut : function(e){
56947         var hd = this.findHeaderCell(e.getTarget());
56948         if(hd){
56949             this.fly(hd).removeClass("x-grid-hd-over");
56950         }
56951     },
56952
56953     handleSplitDblClick : function(e, t){
56954         var i = this.getCellIndex(t);
56955         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56956             this.autoSizeColumn(i, true);
56957             this.layout();
56958         }
56959     },
56960
56961     render : function(){
56962
56963         var cm = this.cm;
56964         var colCount = cm.getColumnCount();
56965
56966         if(this.grid.monitorWindowResize === true){
56967             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56968         }
56969         var header = this.renderHeaders();
56970         var body = this.templates.body.apply({rows:""});
56971         var html = this.templates.master.apply({
56972             lockedBody: body,
56973             body: body,
56974             lockedHeader: header[0],
56975             header: header[1]
56976         });
56977
56978         //this.updateColumns();
56979
56980         this.grid.getGridEl().dom.innerHTML = html;
56981
56982         this.initElements();
56983         
56984         // a kludge to fix the random scolling effect in webkit
56985         this.el.on("scroll", function() {
56986             this.el.dom.scrollTop=0; // hopefully not recursive..
56987         },this);
56988
56989         this.scroller.on("scroll", this.handleScroll, this);
56990         this.lockedBody.on("mousewheel", this.handleWheel, this);
56991         this.mainBody.on("mousewheel", this.handleWheel, this);
56992
56993         this.mainHd.on("mouseover", this.handleHdOver, this);
56994         this.mainHd.on("mouseout", this.handleHdOut, this);
56995         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56996                 {delegate: "."+this.splitClass});
56997
56998         this.lockedHd.on("mouseover", this.handleHdOver, this);
56999         this.lockedHd.on("mouseout", this.handleHdOut, this);
57000         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57001                 {delegate: "."+this.splitClass});
57002
57003         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57004             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57005         }
57006
57007         this.updateSplitters();
57008
57009         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57010             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57011             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57012         }
57013
57014         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57015             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57016             this.hmenu.add(
57017                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57018                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57019             );
57020             if(this.grid.enableColLock !== false){
57021                 this.hmenu.add('-',
57022                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57023                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57024                 );
57025             }
57026             if (Roo.isTouch) {
57027                  this.hmenu.add('-',
57028                     {id:"wider", text: this.columnsWiderText},
57029                     {id:"narrow", text: this.columnsNarrowText }
57030                 );
57031                 
57032                  
57033             }
57034             
57035             if(this.grid.enableColumnHide !== false){
57036
57037                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57038                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57039                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57040
57041                 this.hmenu.add('-',
57042                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57043                 );
57044             }
57045             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57046
57047             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57048         }
57049
57050         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57051             this.dd = new Roo.grid.GridDragZone(this.grid, {
57052                 ddGroup : this.grid.ddGroup || 'GridDD'
57053             });
57054             
57055         }
57056
57057         /*
57058         for(var i = 0; i < colCount; i++){
57059             if(cm.isHidden(i)){
57060                 this.hideColumn(i);
57061             }
57062             if(cm.config[i].align){
57063                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57064                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57065             }
57066         }*/
57067         
57068         this.updateHeaderSortState();
57069
57070         this.beforeInitialResize();
57071         this.layout(true);
57072
57073         // two part rendering gives faster view to the user
57074         this.renderPhase2.defer(1, this);
57075     },
57076
57077     renderPhase2 : function(){
57078         // render the rows now
57079         this.refresh();
57080         if(this.grid.autoSizeColumns){
57081             this.autoSizeColumns();
57082         }
57083     },
57084
57085     beforeInitialResize : function(){
57086
57087     },
57088
57089     onColumnSplitterMoved : function(i, w){
57090         this.userResized = true;
57091         var cm = this.grid.colModel;
57092         cm.setColumnWidth(i, w, true);
57093         var cid = cm.getColumnId(i);
57094         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57095         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57096         this.updateSplitters();
57097         this.layout();
57098         this.grid.fireEvent("columnresize", i, w);
57099     },
57100
57101     syncRowHeights : function(startIndex, endIndex){
57102         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57103             startIndex = startIndex || 0;
57104             var mrows = this.getBodyTable().rows;
57105             var lrows = this.getLockedTable().rows;
57106             var len = mrows.length-1;
57107             endIndex = Math.min(endIndex || len, len);
57108             for(var i = startIndex; i <= endIndex; i++){
57109                 var m = mrows[i], l = lrows[i];
57110                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57111                 m.style.height = l.style.height = h + "px";
57112             }
57113         }
57114     },
57115
57116     layout : function(initialRender, is2ndPass){
57117         var g = this.grid;
57118         var auto = g.autoHeight;
57119         var scrollOffset = 16;
57120         var c = g.getGridEl(), cm = this.cm,
57121                 expandCol = g.autoExpandColumn,
57122                 gv = this;
57123         //c.beginMeasure();
57124
57125         if(!c.dom.offsetWidth){ // display:none?
57126             if(initialRender){
57127                 this.lockedWrap.show();
57128                 this.mainWrap.show();
57129             }
57130             return;
57131         }
57132
57133         var hasLock = this.cm.isLocked(0);
57134
57135         var tbh = this.headerPanel.getHeight();
57136         var bbh = this.footerPanel.getHeight();
57137
57138         if(auto){
57139             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57140             var newHeight = ch + c.getBorderWidth("tb");
57141             if(g.maxHeight){
57142                 newHeight = Math.min(g.maxHeight, newHeight);
57143             }
57144             c.setHeight(newHeight);
57145         }
57146
57147         if(g.autoWidth){
57148             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57149         }
57150
57151         var s = this.scroller;
57152
57153         var csize = c.getSize(true);
57154
57155         this.el.setSize(csize.width, csize.height);
57156
57157         this.headerPanel.setWidth(csize.width);
57158         this.footerPanel.setWidth(csize.width);
57159
57160         var hdHeight = this.mainHd.getHeight();
57161         var vw = csize.width;
57162         var vh = csize.height - (tbh + bbh);
57163
57164         s.setSize(vw, vh);
57165
57166         var bt = this.getBodyTable();
57167         
57168         if(cm.getLockedCount() == cm.config.length){
57169             bt = this.getLockedTable();
57170         }
57171         
57172         var ltWidth = hasLock ?
57173                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57174
57175         var scrollHeight = bt.offsetHeight;
57176         var scrollWidth = ltWidth + bt.offsetWidth;
57177         var vscroll = false, hscroll = false;
57178
57179         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57180
57181         var lw = this.lockedWrap, mw = this.mainWrap;
57182         var lb = this.lockedBody, mb = this.mainBody;
57183
57184         setTimeout(function(){
57185             var t = s.dom.offsetTop;
57186             var w = s.dom.clientWidth,
57187                 h = s.dom.clientHeight;
57188
57189             lw.setTop(t);
57190             lw.setSize(ltWidth, h);
57191
57192             mw.setLeftTop(ltWidth, t);
57193             mw.setSize(w-ltWidth, h);
57194
57195             lb.setHeight(h-hdHeight);
57196             mb.setHeight(h-hdHeight);
57197
57198             if(is2ndPass !== true && !gv.userResized && expandCol){
57199                 // high speed resize without full column calculation
57200                 
57201                 var ci = cm.getIndexById(expandCol);
57202                 if (ci < 0) {
57203                     ci = cm.findColumnIndex(expandCol);
57204                 }
57205                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57206                 var expandId = cm.getColumnId(ci);
57207                 var  tw = cm.getTotalWidth(false);
57208                 var currentWidth = cm.getColumnWidth(ci);
57209                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57210                 if(currentWidth != cw){
57211                     cm.setColumnWidth(ci, cw, true);
57212                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57213                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57214                     gv.updateSplitters();
57215                     gv.layout(false, true);
57216                 }
57217             }
57218
57219             if(initialRender){
57220                 lw.show();
57221                 mw.show();
57222             }
57223             //c.endMeasure();
57224         }, 10);
57225     },
57226
57227     onWindowResize : function(){
57228         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57229             return;
57230         }
57231         this.layout();
57232     },
57233
57234     appendFooter : function(parentEl){
57235         return null;
57236     },
57237
57238     sortAscText : "Sort Ascending",
57239     sortDescText : "Sort Descending",
57240     lockText : "Lock Column",
57241     unlockText : "Unlock Column",
57242     columnsText : "Columns",
57243  
57244     columnsWiderText : "Wider",
57245     columnsNarrowText : "Thinner"
57246 });
57247
57248
57249 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57250     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57251     this.proxy.el.addClass('x-grid3-col-dd');
57252 };
57253
57254 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57255     handleMouseDown : function(e){
57256
57257     },
57258
57259     callHandleMouseDown : function(e){
57260         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57261     }
57262 });
57263 /*
57264  * Based on:
57265  * Ext JS Library 1.1.1
57266  * Copyright(c) 2006-2007, Ext JS, LLC.
57267  *
57268  * Originally Released Under LGPL - original licence link has changed is not relivant.
57269  *
57270  * Fork - LGPL
57271  * <script type="text/javascript">
57272  */
57273  
57274 // private
57275 // This is a support class used internally by the Grid components
57276 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57277     this.grid = grid;
57278     this.view = grid.getView();
57279     this.proxy = this.view.resizeProxy;
57280     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57281         "gridSplitters" + this.grid.getGridEl().id, {
57282         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57283     });
57284     this.setHandleElId(Roo.id(hd));
57285     this.setOuterHandleElId(Roo.id(hd2));
57286     this.scroll = false;
57287 };
57288 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57289     fly: Roo.Element.fly,
57290
57291     b4StartDrag : function(x, y){
57292         this.view.headersDisabled = true;
57293         this.proxy.setHeight(this.view.mainWrap.getHeight());
57294         var w = this.cm.getColumnWidth(this.cellIndex);
57295         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57296         this.resetConstraints();
57297         this.setXConstraint(minw, 1000);
57298         this.setYConstraint(0, 0);
57299         this.minX = x - minw;
57300         this.maxX = x + 1000;
57301         this.startPos = x;
57302         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57303     },
57304
57305
57306     handleMouseDown : function(e){
57307         ev = Roo.EventObject.setEvent(e);
57308         var t = this.fly(ev.getTarget());
57309         if(t.hasClass("x-grid-split")){
57310             this.cellIndex = this.view.getCellIndex(t.dom);
57311             this.split = t.dom;
57312             this.cm = this.grid.colModel;
57313             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57314                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57315             }
57316         }
57317     },
57318
57319     endDrag : function(e){
57320         this.view.headersDisabled = false;
57321         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57322         var diff = endX - this.startPos;
57323         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57324     },
57325
57326     autoOffset : function(){
57327         this.setDelta(0,0);
57328     }
57329 });/*
57330  * Based on:
57331  * Ext JS Library 1.1.1
57332  * Copyright(c) 2006-2007, Ext JS, LLC.
57333  *
57334  * Originally Released Under LGPL - original licence link has changed is not relivant.
57335  *
57336  * Fork - LGPL
57337  * <script type="text/javascript">
57338  */
57339  
57340 // private
57341 // This is a support class used internally by the Grid components
57342 Roo.grid.GridDragZone = function(grid, config){
57343     this.view = grid.getView();
57344     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57345     if(this.view.lockedBody){
57346         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57347         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57348     }
57349     this.scroll = false;
57350     this.grid = grid;
57351     this.ddel = document.createElement('div');
57352     this.ddel.className = 'x-grid-dd-wrap';
57353 };
57354
57355 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57356     ddGroup : "GridDD",
57357
57358     getDragData : function(e){
57359         var t = Roo.lib.Event.getTarget(e);
57360         var rowIndex = this.view.findRowIndex(t);
57361         var sm = this.grid.selModel;
57362             
57363         //Roo.log(rowIndex);
57364         
57365         if (sm.getSelectedCell) {
57366             // cell selection..
57367             if (!sm.getSelectedCell()) {
57368                 return false;
57369             }
57370             if (rowIndex != sm.getSelectedCell()[0]) {
57371                 return false;
57372             }
57373         
57374         }
57375         
57376         if(rowIndex !== false){
57377             
57378             // if editorgrid.. 
57379             
57380             
57381             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57382                
57383             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57384               //  
57385             //}
57386             if (e.hasModifier()){
57387                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57388             }
57389             
57390             Roo.log("getDragData");
57391             
57392             return {
57393                 grid: this.grid,
57394                 ddel: this.ddel,
57395                 rowIndex: rowIndex,
57396                 selections:sm.getSelections ? sm.getSelections() : (
57397                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57398                 )
57399             };
57400         }
57401         return false;
57402     },
57403
57404     onInitDrag : function(e){
57405         var data = this.dragData;
57406         this.ddel.innerHTML = this.grid.getDragDropText();
57407         this.proxy.update(this.ddel);
57408         // fire start drag?
57409     },
57410
57411     afterRepair : function(){
57412         this.dragging = false;
57413     },
57414
57415     getRepairXY : function(e, data){
57416         return false;
57417     },
57418
57419     onEndDrag : function(data, e){
57420         // fire end drag?
57421     },
57422
57423     onValidDrop : function(dd, e, id){
57424         // fire drag drop?
57425         this.hideProxy();
57426     },
57427
57428     beforeInvalidDrop : function(e, id){
57429
57430     }
57431 });/*
57432  * Based on:
57433  * Ext JS Library 1.1.1
57434  * Copyright(c) 2006-2007, Ext JS, LLC.
57435  *
57436  * Originally Released Under LGPL - original licence link has changed is not relivant.
57437  *
57438  * Fork - LGPL
57439  * <script type="text/javascript">
57440  */
57441  
57442
57443 /**
57444  * @class Roo.grid.ColumnModel
57445  * @extends Roo.util.Observable
57446  * This is the default implementation of a ColumnModel used by the Grid. It defines
57447  * the columns in the grid.
57448  * <br>Usage:<br>
57449  <pre><code>
57450  var colModel = new Roo.grid.ColumnModel([
57451         {header: "Ticker", width: 60, sortable: true, locked: true},
57452         {header: "Company Name", width: 150, sortable: true},
57453         {header: "Market Cap.", width: 100, sortable: true},
57454         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57455         {header: "Employees", width: 100, sortable: true, resizable: false}
57456  ]);
57457  </code></pre>
57458  * <p>
57459  
57460  * The config options listed for this class are options which may appear in each
57461  * individual column definition.
57462  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57463  * @constructor
57464  * @param {Object} config An Array of column config objects. See this class's
57465  * config objects for details.
57466 */
57467 Roo.grid.ColumnModel = function(config){
57468         /**
57469      * The config passed into the constructor
57470      */
57471     this.config = config;
57472     this.lookup = {};
57473
57474     // if no id, create one
57475     // if the column does not have a dataIndex mapping,
57476     // map it to the order it is in the config
57477     for(var i = 0, len = config.length; i < len; i++){
57478         var c = config[i];
57479         if(typeof c.dataIndex == "undefined"){
57480             c.dataIndex = i;
57481         }
57482         if(typeof c.renderer == "string"){
57483             c.renderer = Roo.util.Format[c.renderer];
57484         }
57485         if(typeof c.id == "undefined"){
57486             c.id = Roo.id();
57487         }
57488         if(c.editor && c.editor.xtype){
57489             c.editor  = Roo.factory(c.editor, Roo.grid);
57490         }
57491         if(c.editor && c.editor.isFormField){
57492             c.editor = new Roo.grid.GridEditor(c.editor);
57493         }
57494         this.lookup[c.id] = c;
57495     }
57496
57497     /**
57498      * The width of columns which have no width specified (defaults to 100)
57499      * @type Number
57500      */
57501     this.defaultWidth = 100;
57502
57503     /**
57504      * Default sortable of columns which have no sortable specified (defaults to false)
57505      * @type Boolean
57506      */
57507     this.defaultSortable = false;
57508
57509     this.addEvents({
57510         /**
57511              * @event widthchange
57512              * Fires when the width of a column changes.
57513              * @param {ColumnModel} this
57514              * @param {Number} columnIndex The column index
57515              * @param {Number} newWidth The new width
57516              */
57517             "widthchange": true,
57518         /**
57519              * @event headerchange
57520              * Fires when the text of a header changes.
57521              * @param {ColumnModel} this
57522              * @param {Number} columnIndex The column index
57523              * @param {Number} newText The new header text
57524              */
57525             "headerchange": true,
57526         /**
57527              * @event hiddenchange
57528              * Fires when a column is hidden or "unhidden".
57529              * @param {ColumnModel} this
57530              * @param {Number} columnIndex The column index
57531              * @param {Boolean} hidden true if hidden, false otherwise
57532              */
57533             "hiddenchange": true,
57534             /**
57535          * @event columnmoved
57536          * Fires when a column is moved.
57537          * @param {ColumnModel} this
57538          * @param {Number} oldIndex
57539          * @param {Number} newIndex
57540          */
57541         "columnmoved" : true,
57542         /**
57543          * @event columlockchange
57544          * Fires when a column's locked state is changed
57545          * @param {ColumnModel} this
57546          * @param {Number} colIndex
57547          * @param {Boolean} locked true if locked
57548          */
57549         "columnlockchange" : true
57550     });
57551     Roo.grid.ColumnModel.superclass.constructor.call(this);
57552 };
57553 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57554     /**
57555      * @cfg {String} header The header text to display in the Grid view.
57556      */
57557     /**
57558      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57559      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57560      * specified, the column's index is used as an index into the Record's data Array.
57561      */
57562     /**
57563      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57564      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57565      */
57566     /**
57567      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57568      * Defaults to the value of the {@link #defaultSortable} property.
57569      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57570      */
57571     /**
57572      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57573      */
57574     /**
57575      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57576      */
57577     /**
57578      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57579      */
57580     /**
57581      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57582      */
57583     /**
57584      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57585      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57586      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57587      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57588      */
57589        /**
57590      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57591      */
57592     /**
57593      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57594      */
57595     /**
57596      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57597      */
57598     /**
57599      * @cfg {String} cursor (Optional)
57600      */
57601     /**
57602      * @cfg {String} tooltip (Optional)
57603      */
57604     /**
57605      * @cfg {Number} xs (Optional)
57606      */
57607     /**
57608      * @cfg {Number} sm (Optional)
57609      */
57610     /**
57611      * @cfg {Number} md (Optional)
57612      */
57613     /**
57614      * @cfg {Number} lg (Optional)
57615      */
57616     /**
57617      * Returns the id of the column at the specified index.
57618      * @param {Number} index The column index
57619      * @return {String} the id
57620      */
57621     getColumnId : function(index){
57622         return this.config[index].id;
57623     },
57624
57625     /**
57626      * Returns the column for a specified id.
57627      * @param {String} id The column id
57628      * @return {Object} the column
57629      */
57630     getColumnById : function(id){
57631         return this.lookup[id];
57632     },
57633
57634     
57635     /**
57636      * Returns the column for a specified dataIndex.
57637      * @param {String} dataIndex The column dataIndex
57638      * @return {Object|Boolean} the column or false if not found
57639      */
57640     getColumnByDataIndex: function(dataIndex){
57641         var index = this.findColumnIndex(dataIndex);
57642         return index > -1 ? this.config[index] : false;
57643     },
57644     
57645     /**
57646      * Returns the index for a specified column id.
57647      * @param {String} id The column id
57648      * @return {Number} the index, or -1 if not found
57649      */
57650     getIndexById : function(id){
57651         for(var i = 0, len = this.config.length; i < len; i++){
57652             if(this.config[i].id == id){
57653                 return i;
57654             }
57655         }
57656         return -1;
57657     },
57658     
57659     /**
57660      * Returns the index for a specified column dataIndex.
57661      * @param {String} dataIndex The column dataIndex
57662      * @return {Number} the index, or -1 if not found
57663      */
57664     
57665     findColumnIndex : function(dataIndex){
57666         for(var i = 0, len = this.config.length; i < len; i++){
57667             if(this.config[i].dataIndex == dataIndex){
57668                 return i;
57669             }
57670         }
57671         return -1;
57672     },
57673     
57674     
57675     moveColumn : function(oldIndex, newIndex){
57676         var c = this.config[oldIndex];
57677         this.config.splice(oldIndex, 1);
57678         this.config.splice(newIndex, 0, c);
57679         this.dataMap = null;
57680         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57681     },
57682
57683     isLocked : function(colIndex){
57684         return this.config[colIndex].locked === true;
57685     },
57686
57687     setLocked : function(colIndex, value, suppressEvent){
57688         if(this.isLocked(colIndex) == value){
57689             return;
57690         }
57691         this.config[colIndex].locked = value;
57692         if(!suppressEvent){
57693             this.fireEvent("columnlockchange", this, colIndex, value);
57694         }
57695     },
57696
57697     getTotalLockedWidth : function(){
57698         var totalWidth = 0;
57699         for(var i = 0; i < this.config.length; i++){
57700             if(this.isLocked(i) && !this.isHidden(i)){
57701                 this.totalWidth += this.getColumnWidth(i);
57702             }
57703         }
57704         return totalWidth;
57705     },
57706
57707     getLockedCount : function(){
57708         for(var i = 0, len = this.config.length; i < len; i++){
57709             if(!this.isLocked(i)){
57710                 return i;
57711             }
57712         }
57713         
57714         return this.config.length;
57715     },
57716
57717     /**
57718      * Returns the number of columns.
57719      * @return {Number}
57720      */
57721     getColumnCount : function(visibleOnly){
57722         if(visibleOnly === true){
57723             var c = 0;
57724             for(var i = 0, len = this.config.length; i < len; i++){
57725                 if(!this.isHidden(i)){
57726                     c++;
57727                 }
57728             }
57729             return c;
57730         }
57731         return this.config.length;
57732     },
57733
57734     /**
57735      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57736      * @param {Function} fn
57737      * @param {Object} scope (optional)
57738      * @return {Array} result
57739      */
57740     getColumnsBy : function(fn, scope){
57741         var r = [];
57742         for(var i = 0, len = this.config.length; i < len; i++){
57743             var c = this.config[i];
57744             if(fn.call(scope||this, c, i) === true){
57745                 r[r.length] = c;
57746             }
57747         }
57748         return r;
57749     },
57750
57751     /**
57752      * Returns true if the specified column is sortable.
57753      * @param {Number} col The column index
57754      * @return {Boolean}
57755      */
57756     isSortable : function(col){
57757         if(typeof this.config[col].sortable == "undefined"){
57758             return this.defaultSortable;
57759         }
57760         return this.config[col].sortable;
57761     },
57762
57763     /**
57764      * Returns the rendering (formatting) function defined for the column.
57765      * @param {Number} col The column index.
57766      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57767      */
57768     getRenderer : function(col){
57769         if(!this.config[col].renderer){
57770             return Roo.grid.ColumnModel.defaultRenderer;
57771         }
57772         return this.config[col].renderer;
57773     },
57774
57775     /**
57776      * Sets the rendering (formatting) function for a column.
57777      * @param {Number} col The column index
57778      * @param {Function} fn The function to use to process the cell's raw data
57779      * to return HTML markup for the grid view. The render function is called with
57780      * the following parameters:<ul>
57781      * <li>Data value.</li>
57782      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57783      * <li>css A CSS style string to apply to the table cell.</li>
57784      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57785      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57786      * <li>Row index</li>
57787      * <li>Column index</li>
57788      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57789      */
57790     setRenderer : function(col, fn){
57791         this.config[col].renderer = fn;
57792     },
57793
57794     /**
57795      * Returns the width for the specified column.
57796      * @param {Number} col The column index
57797      * @return {Number}
57798      */
57799     getColumnWidth : function(col){
57800         return this.config[col].width * 1 || this.defaultWidth;
57801     },
57802
57803     /**
57804      * Sets the width for a column.
57805      * @param {Number} col The column index
57806      * @param {Number} width The new width
57807      */
57808     setColumnWidth : function(col, width, suppressEvent){
57809         this.config[col].width = width;
57810         this.totalWidth = null;
57811         if(!suppressEvent){
57812              this.fireEvent("widthchange", this, col, width);
57813         }
57814     },
57815
57816     /**
57817      * Returns the total width of all columns.
57818      * @param {Boolean} includeHidden True to include hidden column widths
57819      * @return {Number}
57820      */
57821     getTotalWidth : function(includeHidden){
57822         if(!this.totalWidth){
57823             this.totalWidth = 0;
57824             for(var i = 0, len = this.config.length; i < len; i++){
57825                 if(includeHidden || !this.isHidden(i)){
57826                     this.totalWidth += this.getColumnWidth(i);
57827                 }
57828             }
57829         }
57830         return this.totalWidth;
57831     },
57832
57833     /**
57834      * Returns the header for the specified column.
57835      * @param {Number} col The column index
57836      * @return {String}
57837      */
57838     getColumnHeader : function(col){
57839         return this.config[col].header;
57840     },
57841
57842     /**
57843      * Sets the header for a column.
57844      * @param {Number} col The column index
57845      * @param {String} header The new header
57846      */
57847     setColumnHeader : function(col, header){
57848         this.config[col].header = header;
57849         this.fireEvent("headerchange", this, col, header);
57850     },
57851
57852     /**
57853      * Returns the tooltip for the specified column.
57854      * @param {Number} col The column index
57855      * @return {String}
57856      */
57857     getColumnTooltip : function(col){
57858             return this.config[col].tooltip;
57859     },
57860     /**
57861      * Sets the tooltip for a column.
57862      * @param {Number} col The column index
57863      * @param {String} tooltip The new tooltip
57864      */
57865     setColumnTooltip : function(col, tooltip){
57866             this.config[col].tooltip = tooltip;
57867     },
57868
57869     /**
57870      * Returns the dataIndex for the specified column.
57871      * @param {Number} col The column index
57872      * @return {Number}
57873      */
57874     getDataIndex : function(col){
57875         return this.config[col].dataIndex;
57876     },
57877
57878     /**
57879      * Sets the dataIndex for a column.
57880      * @param {Number} col The column index
57881      * @param {Number} dataIndex The new dataIndex
57882      */
57883     setDataIndex : function(col, dataIndex){
57884         this.config[col].dataIndex = dataIndex;
57885     },
57886
57887     
57888     
57889     /**
57890      * Returns true if the cell is editable.
57891      * @param {Number} colIndex The column index
57892      * @param {Number} rowIndex The row index - this is nto actually used..?
57893      * @return {Boolean}
57894      */
57895     isCellEditable : function(colIndex, rowIndex){
57896         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57897     },
57898
57899     /**
57900      * Returns the editor defined for the cell/column.
57901      * return false or null to disable editing.
57902      * @param {Number} colIndex The column index
57903      * @param {Number} rowIndex The row index
57904      * @return {Object}
57905      */
57906     getCellEditor : function(colIndex, rowIndex){
57907         return this.config[colIndex].editor;
57908     },
57909
57910     /**
57911      * Sets if a column is editable.
57912      * @param {Number} col The column index
57913      * @param {Boolean} editable True if the column is editable
57914      */
57915     setEditable : function(col, editable){
57916         this.config[col].editable = editable;
57917     },
57918
57919
57920     /**
57921      * Returns true if the column is hidden.
57922      * @param {Number} colIndex The column index
57923      * @return {Boolean}
57924      */
57925     isHidden : function(colIndex){
57926         return this.config[colIndex].hidden;
57927     },
57928
57929
57930     /**
57931      * Returns true if the column width cannot be changed
57932      */
57933     isFixed : function(colIndex){
57934         return this.config[colIndex].fixed;
57935     },
57936
57937     /**
57938      * Returns true if the column can be resized
57939      * @return {Boolean}
57940      */
57941     isResizable : function(colIndex){
57942         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57943     },
57944     /**
57945      * Sets if a column is hidden.
57946      * @param {Number} colIndex The column index
57947      * @param {Boolean} hidden True if the column is hidden
57948      */
57949     setHidden : function(colIndex, hidden){
57950         this.config[colIndex].hidden = hidden;
57951         this.totalWidth = null;
57952         this.fireEvent("hiddenchange", this, colIndex, hidden);
57953     },
57954
57955     /**
57956      * Sets the editor for a column.
57957      * @param {Number} col The column index
57958      * @param {Object} editor The editor object
57959      */
57960     setEditor : function(col, editor){
57961         this.config[col].editor = editor;
57962     }
57963 });
57964
57965 Roo.grid.ColumnModel.defaultRenderer = function(value)
57966 {
57967     if(typeof value == "object") {
57968         return value;
57969     }
57970         if(typeof value == "string" && value.length < 1){
57971             return "&#160;";
57972         }
57973     
57974         return String.format("{0}", value);
57975 };
57976
57977 // Alias for backwards compatibility
57978 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57979 /*
57980  * Based on:
57981  * Ext JS Library 1.1.1
57982  * Copyright(c) 2006-2007, Ext JS, LLC.
57983  *
57984  * Originally Released Under LGPL - original licence link has changed is not relivant.
57985  *
57986  * Fork - LGPL
57987  * <script type="text/javascript">
57988  */
57989
57990 /**
57991  * @class Roo.grid.AbstractSelectionModel
57992  * @extends Roo.util.Observable
57993  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57994  * implemented by descendant classes.  This class should not be directly instantiated.
57995  * @constructor
57996  */
57997 Roo.grid.AbstractSelectionModel = function(){
57998     this.locked = false;
57999     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58000 };
58001
58002 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58003     /** @ignore Called by the grid automatically. Do not call directly. */
58004     init : function(grid){
58005         this.grid = grid;
58006         this.initEvents();
58007     },
58008
58009     /**
58010      * Locks the selections.
58011      */
58012     lock : function(){
58013         this.locked = true;
58014     },
58015
58016     /**
58017      * Unlocks the selections.
58018      */
58019     unlock : function(){
58020         this.locked = false;
58021     },
58022
58023     /**
58024      * Returns true if the selections are locked.
58025      * @return {Boolean}
58026      */
58027     isLocked : function(){
58028         return this.locked;
58029     }
58030 });/*
58031  * Based on:
58032  * Ext JS Library 1.1.1
58033  * Copyright(c) 2006-2007, Ext JS, LLC.
58034  *
58035  * Originally Released Under LGPL - original licence link has changed is not relivant.
58036  *
58037  * Fork - LGPL
58038  * <script type="text/javascript">
58039  */
58040 /**
58041  * @extends Roo.grid.AbstractSelectionModel
58042  * @class Roo.grid.RowSelectionModel
58043  * The default SelectionModel used by {@link Roo.grid.Grid}.
58044  * It supports multiple selections and keyboard selection/navigation. 
58045  * @constructor
58046  * @param {Object} config
58047  */
58048 Roo.grid.RowSelectionModel = function(config){
58049     Roo.apply(this, config);
58050     this.selections = new Roo.util.MixedCollection(false, function(o){
58051         return o.id;
58052     });
58053
58054     this.last = false;
58055     this.lastActive = false;
58056
58057     this.addEvents({
58058         /**
58059              * @event selectionchange
58060              * Fires when the selection changes
58061              * @param {SelectionModel} this
58062              */
58063             "selectionchange" : true,
58064         /**
58065              * @event afterselectionchange
58066              * Fires after the selection changes (eg. by key press or clicking)
58067              * @param {SelectionModel} this
58068              */
58069             "afterselectionchange" : true,
58070         /**
58071              * @event beforerowselect
58072              * Fires when a row is selected being selected, return false to cancel.
58073              * @param {SelectionModel} this
58074              * @param {Number} rowIndex The selected index
58075              * @param {Boolean} keepExisting False if other selections will be cleared
58076              */
58077             "beforerowselect" : true,
58078         /**
58079              * @event rowselect
58080              * Fires when a row is selected.
58081              * @param {SelectionModel} this
58082              * @param {Number} rowIndex The selected index
58083              * @param {Roo.data.Record} r The record
58084              */
58085             "rowselect" : true,
58086         /**
58087              * @event rowdeselect
58088              * Fires when a row is deselected.
58089              * @param {SelectionModel} this
58090              * @param {Number} rowIndex The selected index
58091              */
58092         "rowdeselect" : true
58093     });
58094     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58095     this.locked = false;
58096 };
58097
58098 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58099     /**
58100      * @cfg {Boolean} singleSelect
58101      * True to allow selection of only one row at a time (defaults to false)
58102      */
58103     singleSelect : false,
58104
58105     // private
58106     initEvents : function(){
58107
58108         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58109             this.grid.on("mousedown", this.handleMouseDown, this);
58110         }else{ // allow click to work like normal
58111             this.grid.on("rowclick", this.handleDragableRowClick, this);
58112         }
58113
58114         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58115             "up" : function(e){
58116                 if(!e.shiftKey){
58117                     this.selectPrevious(e.shiftKey);
58118                 }else if(this.last !== false && this.lastActive !== false){
58119                     var last = this.last;
58120                     this.selectRange(this.last,  this.lastActive-1);
58121                     this.grid.getView().focusRow(this.lastActive);
58122                     if(last !== false){
58123                         this.last = last;
58124                     }
58125                 }else{
58126                     this.selectFirstRow();
58127                 }
58128                 this.fireEvent("afterselectionchange", this);
58129             },
58130             "down" : function(e){
58131                 if(!e.shiftKey){
58132                     this.selectNext(e.shiftKey);
58133                 }else if(this.last !== false && this.lastActive !== false){
58134                     var last = this.last;
58135                     this.selectRange(this.last,  this.lastActive+1);
58136                     this.grid.getView().focusRow(this.lastActive);
58137                     if(last !== false){
58138                         this.last = last;
58139                     }
58140                 }else{
58141                     this.selectFirstRow();
58142                 }
58143                 this.fireEvent("afterselectionchange", this);
58144             },
58145             scope: this
58146         });
58147
58148         var view = this.grid.view;
58149         view.on("refresh", this.onRefresh, this);
58150         view.on("rowupdated", this.onRowUpdated, this);
58151         view.on("rowremoved", this.onRemove, this);
58152     },
58153
58154     // private
58155     onRefresh : function(){
58156         var ds = this.grid.dataSource, i, v = this.grid.view;
58157         var s = this.selections;
58158         s.each(function(r){
58159             if((i = ds.indexOfId(r.id)) != -1){
58160                 v.onRowSelect(i);
58161                 s.add(ds.getAt(i)); // updating the selection relate data
58162             }else{
58163                 s.remove(r);
58164             }
58165         });
58166     },
58167
58168     // private
58169     onRemove : function(v, index, r){
58170         this.selections.remove(r);
58171     },
58172
58173     // private
58174     onRowUpdated : function(v, index, r){
58175         if(this.isSelected(r)){
58176             v.onRowSelect(index);
58177         }
58178     },
58179
58180     /**
58181      * Select records.
58182      * @param {Array} records The records to select
58183      * @param {Boolean} keepExisting (optional) True to keep existing selections
58184      */
58185     selectRecords : function(records, keepExisting){
58186         if(!keepExisting){
58187             this.clearSelections();
58188         }
58189         var ds = this.grid.dataSource;
58190         for(var i = 0, len = records.length; i < len; i++){
58191             this.selectRow(ds.indexOf(records[i]), true);
58192         }
58193     },
58194
58195     /**
58196      * Gets the number of selected rows.
58197      * @return {Number}
58198      */
58199     getCount : function(){
58200         return this.selections.length;
58201     },
58202
58203     /**
58204      * Selects the first row in the grid.
58205      */
58206     selectFirstRow : function(){
58207         this.selectRow(0);
58208     },
58209
58210     /**
58211      * Select the last row.
58212      * @param {Boolean} keepExisting (optional) True to keep existing selections
58213      */
58214     selectLastRow : function(keepExisting){
58215         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58216     },
58217
58218     /**
58219      * Selects the row immediately following the last selected row.
58220      * @param {Boolean} keepExisting (optional) True to keep existing selections
58221      */
58222     selectNext : function(keepExisting){
58223         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58224             this.selectRow(this.last+1, keepExisting);
58225             this.grid.getView().focusRow(this.last);
58226         }
58227     },
58228
58229     /**
58230      * Selects the row that precedes the last selected row.
58231      * @param {Boolean} keepExisting (optional) True to keep existing selections
58232      */
58233     selectPrevious : function(keepExisting){
58234         if(this.last){
58235             this.selectRow(this.last-1, keepExisting);
58236             this.grid.getView().focusRow(this.last);
58237         }
58238     },
58239
58240     /**
58241      * Returns the selected records
58242      * @return {Array} Array of selected records
58243      */
58244     getSelections : function(){
58245         return [].concat(this.selections.items);
58246     },
58247
58248     /**
58249      * Returns the first selected record.
58250      * @return {Record}
58251      */
58252     getSelected : function(){
58253         return this.selections.itemAt(0);
58254     },
58255
58256
58257     /**
58258      * Clears all selections.
58259      */
58260     clearSelections : function(fast){
58261         if(this.locked) {
58262             return;
58263         }
58264         if(fast !== true){
58265             var ds = this.grid.dataSource;
58266             var s = this.selections;
58267             s.each(function(r){
58268                 this.deselectRow(ds.indexOfId(r.id));
58269             }, this);
58270             s.clear();
58271         }else{
58272             this.selections.clear();
58273         }
58274         this.last = false;
58275     },
58276
58277
58278     /**
58279      * Selects all rows.
58280      */
58281     selectAll : function(){
58282         if(this.locked) {
58283             return;
58284         }
58285         this.selections.clear();
58286         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58287             this.selectRow(i, true);
58288         }
58289     },
58290
58291     /**
58292      * Returns True if there is a selection.
58293      * @return {Boolean}
58294      */
58295     hasSelection : function(){
58296         return this.selections.length > 0;
58297     },
58298
58299     /**
58300      * Returns True if the specified row is selected.
58301      * @param {Number/Record} record The record or index of the record to check
58302      * @return {Boolean}
58303      */
58304     isSelected : function(index){
58305         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58306         return (r && this.selections.key(r.id) ? true : false);
58307     },
58308
58309     /**
58310      * Returns True if the specified record id is selected.
58311      * @param {String} id The id of record to check
58312      * @return {Boolean}
58313      */
58314     isIdSelected : function(id){
58315         return (this.selections.key(id) ? true : false);
58316     },
58317
58318     // private
58319     handleMouseDown : function(e, t){
58320         var view = this.grid.getView(), rowIndex;
58321         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58322             return;
58323         };
58324         if(e.shiftKey && this.last !== false){
58325             var last = this.last;
58326             this.selectRange(last, rowIndex, e.ctrlKey);
58327             this.last = last; // reset the last
58328             view.focusRow(rowIndex);
58329         }else{
58330             var isSelected = this.isSelected(rowIndex);
58331             if(e.button !== 0 && isSelected){
58332                 view.focusRow(rowIndex);
58333             }else if(e.ctrlKey && isSelected){
58334                 this.deselectRow(rowIndex);
58335             }else if(!isSelected){
58336                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58337                 view.focusRow(rowIndex);
58338             }
58339         }
58340         this.fireEvent("afterselectionchange", this);
58341     },
58342     // private
58343     handleDragableRowClick :  function(grid, rowIndex, e) 
58344     {
58345         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58346             this.selectRow(rowIndex, false);
58347             grid.view.focusRow(rowIndex);
58348              this.fireEvent("afterselectionchange", this);
58349         }
58350     },
58351     
58352     /**
58353      * Selects multiple rows.
58354      * @param {Array} rows Array of the indexes of the row to select
58355      * @param {Boolean} keepExisting (optional) True to keep existing selections
58356      */
58357     selectRows : function(rows, keepExisting){
58358         if(!keepExisting){
58359             this.clearSelections();
58360         }
58361         for(var i = 0, len = rows.length; i < len; i++){
58362             this.selectRow(rows[i], true);
58363         }
58364     },
58365
58366     /**
58367      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58368      * @param {Number} startRow The index of the first row in the range
58369      * @param {Number} endRow The index of the last row in the range
58370      * @param {Boolean} keepExisting (optional) True to retain existing selections
58371      */
58372     selectRange : function(startRow, endRow, keepExisting){
58373         if(this.locked) {
58374             return;
58375         }
58376         if(!keepExisting){
58377             this.clearSelections();
58378         }
58379         if(startRow <= endRow){
58380             for(var i = startRow; i <= endRow; i++){
58381                 this.selectRow(i, true);
58382             }
58383         }else{
58384             for(var i = startRow; i >= endRow; i--){
58385                 this.selectRow(i, true);
58386             }
58387         }
58388     },
58389
58390     /**
58391      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58392      * @param {Number} startRow The index of the first row in the range
58393      * @param {Number} endRow The index of the last row in the range
58394      */
58395     deselectRange : function(startRow, endRow, preventViewNotify){
58396         if(this.locked) {
58397             return;
58398         }
58399         for(var i = startRow; i <= endRow; i++){
58400             this.deselectRow(i, preventViewNotify);
58401         }
58402     },
58403
58404     /**
58405      * Selects a row.
58406      * @param {Number} row The index of the row to select
58407      * @param {Boolean} keepExisting (optional) True to keep existing selections
58408      */
58409     selectRow : function(index, keepExisting, preventViewNotify){
58410         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58411             return;
58412         }
58413         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58414             if(!keepExisting || this.singleSelect){
58415                 this.clearSelections();
58416             }
58417             var r = this.grid.dataSource.getAt(index);
58418             this.selections.add(r);
58419             this.last = this.lastActive = index;
58420             if(!preventViewNotify){
58421                 this.grid.getView().onRowSelect(index);
58422             }
58423             this.fireEvent("rowselect", this, index, r);
58424             this.fireEvent("selectionchange", this);
58425         }
58426     },
58427
58428     /**
58429      * Deselects a row.
58430      * @param {Number} row The index of the row to deselect
58431      */
58432     deselectRow : function(index, preventViewNotify){
58433         if(this.locked) {
58434             return;
58435         }
58436         if(this.last == index){
58437             this.last = false;
58438         }
58439         if(this.lastActive == index){
58440             this.lastActive = false;
58441         }
58442         var r = this.grid.dataSource.getAt(index);
58443         this.selections.remove(r);
58444         if(!preventViewNotify){
58445             this.grid.getView().onRowDeselect(index);
58446         }
58447         this.fireEvent("rowdeselect", this, index);
58448         this.fireEvent("selectionchange", this);
58449     },
58450
58451     // private
58452     restoreLast : function(){
58453         if(this._last){
58454             this.last = this._last;
58455         }
58456     },
58457
58458     // private
58459     acceptsNav : function(row, col, cm){
58460         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58461     },
58462
58463     // private
58464     onEditorKey : function(field, e){
58465         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58466         if(k == e.TAB){
58467             e.stopEvent();
58468             ed.completeEdit();
58469             if(e.shiftKey){
58470                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58471             }else{
58472                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58473             }
58474         }else if(k == e.ENTER && !e.ctrlKey){
58475             e.stopEvent();
58476             ed.completeEdit();
58477             if(e.shiftKey){
58478                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58479             }else{
58480                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58481             }
58482         }else if(k == e.ESC){
58483             ed.cancelEdit();
58484         }
58485         if(newCell){
58486             g.startEditing(newCell[0], newCell[1]);
58487         }
58488     }
58489 });/*
58490  * Based on:
58491  * Ext JS Library 1.1.1
58492  * Copyright(c) 2006-2007, Ext JS, LLC.
58493  *
58494  * Originally Released Under LGPL - original licence link has changed is not relivant.
58495  *
58496  * Fork - LGPL
58497  * <script type="text/javascript">
58498  */
58499 /**
58500  * @class Roo.grid.CellSelectionModel
58501  * @extends Roo.grid.AbstractSelectionModel
58502  * This class provides the basic implementation for cell selection in a grid.
58503  * @constructor
58504  * @param {Object} config The object containing the configuration of this model.
58505  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58506  */
58507 Roo.grid.CellSelectionModel = function(config){
58508     Roo.apply(this, config);
58509
58510     this.selection = null;
58511
58512     this.addEvents({
58513         /**
58514              * @event beforerowselect
58515              * Fires before a cell is selected.
58516              * @param {SelectionModel} this
58517              * @param {Number} rowIndex The selected row index
58518              * @param {Number} colIndex The selected cell index
58519              */
58520             "beforecellselect" : true,
58521         /**
58522              * @event cellselect
58523              * Fires when a cell is selected.
58524              * @param {SelectionModel} this
58525              * @param {Number} rowIndex The selected row index
58526              * @param {Number} colIndex The selected cell index
58527              */
58528             "cellselect" : true,
58529         /**
58530              * @event selectionchange
58531              * Fires when the active selection changes.
58532              * @param {SelectionModel} this
58533              * @param {Object} selection null for no selection or an object (o) with two properties
58534                 <ul>
58535                 <li>o.record: the record object for the row the selection is in</li>
58536                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58537                 </ul>
58538              */
58539             "selectionchange" : true,
58540         /**
58541              * @event tabend
58542              * Fires when the tab (or enter) was pressed on the last editable cell
58543              * You can use this to trigger add new row.
58544              * @param {SelectionModel} this
58545              */
58546             "tabend" : true,
58547          /**
58548              * @event beforeeditnext
58549              * Fires before the next editable sell is made active
58550              * You can use this to skip to another cell or fire the tabend
58551              *    if you set cell to false
58552              * @param {Object} eventdata object : { cell : [ row, col ] } 
58553              */
58554             "beforeeditnext" : true
58555     });
58556     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58557 };
58558
58559 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58560     
58561     enter_is_tab: false,
58562
58563     /** @ignore */
58564     initEvents : function(){
58565         this.grid.on("mousedown", this.handleMouseDown, this);
58566         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58567         var view = this.grid.view;
58568         view.on("refresh", this.onViewChange, this);
58569         view.on("rowupdated", this.onRowUpdated, this);
58570         view.on("beforerowremoved", this.clearSelections, this);
58571         view.on("beforerowsinserted", this.clearSelections, this);
58572         if(this.grid.isEditor){
58573             this.grid.on("beforeedit", this.beforeEdit,  this);
58574         }
58575     },
58576
58577         //private
58578     beforeEdit : function(e){
58579         this.select(e.row, e.column, false, true, e.record);
58580     },
58581
58582         //private
58583     onRowUpdated : function(v, index, r){
58584         if(this.selection && this.selection.record == r){
58585             v.onCellSelect(index, this.selection.cell[1]);
58586         }
58587     },
58588
58589         //private
58590     onViewChange : function(){
58591         this.clearSelections(true);
58592     },
58593
58594         /**
58595          * Returns the currently selected cell,.
58596          * @return {Array} The selected cell (row, column) or null if none selected.
58597          */
58598     getSelectedCell : function(){
58599         return this.selection ? this.selection.cell : null;
58600     },
58601
58602     /**
58603      * Clears all selections.
58604      * @param {Boolean} true to prevent the gridview from being notified about the change.
58605      */
58606     clearSelections : function(preventNotify){
58607         var s = this.selection;
58608         if(s){
58609             if(preventNotify !== true){
58610                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58611             }
58612             this.selection = null;
58613             this.fireEvent("selectionchange", this, null);
58614         }
58615     },
58616
58617     /**
58618      * Returns true if there is a selection.
58619      * @return {Boolean}
58620      */
58621     hasSelection : function(){
58622         return this.selection ? true : false;
58623     },
58624
58625     /** @ignore */
58626     handleMouseDown : function(e, t){
58627         var v = this.grid.getView();
58628         if(this.isLocked()){
58629             return;
58630         };
58631         var row = v.findRowIndex(t);
58632         var cell = v.findCellIndex(t);
58633         if(row !== false && cell !== false){
58634             this.select(row, cell);
58635         }
58636     },
58637
58638     /**
58639      * Selects a cell.
58640      * @param {Number} rowIndex
58641      * @param {Number} collIndex
58642      */
58643     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58644         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58645             this.clearSelections();
58646             r = r || this.grid.dataSource.getAt(rowIndex);
58647             this.selection = {
58648                 record : r,
58649                 cell : [rowIndex, colIndex]
58650             };
58651             if(!preventViewNotify){
58652                 var v = this.grid.getView();
58653                 v.onCellSelect(rowIndex, colIndex);
58654                 if(preventFocus !== true){
58655                     v.focusCell(rowIndex, colIndex);
58656                 }
58657             }
58658             this.fireEvent("cellselect", this, rowIndex, colIndex);
58659             this.fireEvent("selectionchange", this, this.selection);
58660         }
58661     },
58662
58663         //private
58664     isSelectable : function(rowIndex, colIndex, cm){
58665         return !cm.isHidden(colIndex);
58666     },
58667
58668     /** @ignore */
58669     handleKeyDown : function(e){
58670         //Roo.log('Cell Sel Model handleKeyDown');
58671         if(!e.isNavKeyPress()){
58672             return;
58673         }
58674         var g = this.grid, s = this.selection;
58675         if(!s){
58676             e.stopEvent();
58677             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58678             if(cell){
58679                 this.select(cell[0], cell[1]);
58680             }
58681             return;
58682         }
58683         var sm = this;
58684         var walk = function(row, col, step){
58685             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58686         };
58687         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58688         var newCell;
58689
58690       
58691
58692         switch(k){
58693             case e.TAB:
58694                 // handled by onEditorKey
58695                 if (g.isEditor && g.editing) {
58696                     return;
58697                 }
58698                 if(e.shiftKey) {
58699                     newCell = walk(r, c-1, -1);
58700                 } else {
58701                     newCell = walk(r, c+1, 1);
58702                 }
58703                 break;
58704             
58705             case e.DOWN:
58706                newCell = walk(r+1, c, 1);
58707                 break;
58708             
58709             case e.UP:
58710                 newCell = walk(r-1, c, -1);
58711                 break;
58712             
58713             case e.RIGHT:
58714                 newCell = walk(r, c+1, 1);
58715                 break;
58716             
58717             case e.LEFT:
58718                 newCell = walk(r, c-1, -1);
58719                 break;
58720             
58721             case e.ENTER:
58722                 
58723                 if(g.isEditor && !g.editing){
58724                    g.startEditing(r, c);
58725                    e.stopEvent();
58726                    return;
58727                 }
58728                 
58729                 
58730              break;
58731         };
58732         if(newCell){
58733             this.select(newCell[0], newCell[1]);
58734             e.stopEvent();
58735             
58736         }
58737     },
58738
58739     acceptsNav : function(row, col, cm){
58740         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58741     },
58742     /**
58743      * Selects a cell.
58744      * @param {Number} field (not used) - as it's normally used as a listener
58745      * @param {Number} e - event - fake it by using
58746      *
58747      * var e = Roo.EventObjectImpl.prototype;
58748      * e.keyCode = e.TAB
58749      *
58750      * 
58751      */
58752     onEditorKey : function(field, e){
58753         
58754         var k = e.getKey(),
58755             newCell,
58756             g = this.grid,
58757             ed = g.activeEditor,
58758             forward = false;
58759         ///Roo.log('onEditorKey' + k);
58760         
58761         
58762         if (this.enter_is_tab && k == e.ENTER) {
58763             k = e.TAB;
58764         }
58765         
58766         if(k == e.TAB){
58767             if(e.shiftKey){
58768                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58769             }else{
58770                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58771                 forward = true;
58772             }
58773             
58774             e.stopEvent();
58775             
58776         } else if(k == e.ENTER &&  !e.ctrlKey){
58777             ed.completeEdit();
58778             e.stopEvent();
58779             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58780         
58781                 } else if(k == e.ESC){
58782             ed.cancelEdit();
58783         }
58784                 
58785         if (newCell) {
58786             var ecall = { cell : newCell, forward : forward };
58787             this.fireEvent('beforeeditnext', ecall );
58788             newCell = ecall.cell;
58789                         forward = ecall.forward;
58790         }
58791                 
58792         if(newCell){
58793             //Roo.log('next cell after edit');
58794             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58795         } else if (forward) {
58796             // tabbed past last
58797             this.fireEvent.defer(100, this, ['tabend',this]);
58798         }
58799     }
58800 });/*
58801  * Based on:
58802  * Ext JS Library 1.1.1
58803  * Copyright(c) 2006-2007, Ext JS, LLC.
58804  *
58805  * Originally Released Under LGPL - original licence link has changed is not relivant.
58806  *
58807  * Fork - LGPL
58808  * <script type="text/javascript">
58809  */
58810  
58811 /**
58812  * @class Roo.grid.EditorGrid
58813  * @extends Roo.grid.Grid
58814  * Class for creating and editable grid.
58815  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58816  * The container MUST have some type of size defined for the grid to fill. The container will be 
58817  * automatically set to position relative if it isn't already.
58818  * @param {Object} dataSource The data model to bind to
58819  * @param {Object} colModel The column model with info about this grid's columns
58820  */
58821 Roo.grid.EditorGrid = function(container, config){
58822     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58823     this.getGridEl().addClass("xedit-grid");
58824
58825     if(!this.selModel){
58826         this.selModel = new Roo.grid.CellSelectionModel();
58827     }
58828
58829     this.activeEditor = null;
58830
58831         this.addEvents({
58832             /**
58833              * @event beforeedit
58834              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58835              * <ul style="padding:5px;padding-left:16px;">
58836              * <li>grid - This grid</li>
58837              * <li>record - The record being edited</li>
58838              * <li>field - The field name being edited</li>
58839              * <li>value - The value for the field being edited.</li>
58840              * <li>row - The grid row index</li>
58841              * <li>column - The grid column index</li>
58842              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58843              * </ul>
58844              * @param {Object} e An edit event (see above for description)
58845              */
58846             "beforeedit" : true,
58847             /**
58848              * @event afteredit
58849              * Fires after a cell is edited. <br />
58850              * <ul style="padding:5px;padding-left:16px;">
58851              * <li>grid - This grid</li>
58852              * <li>record - The record being edited</li>
58853              * <li>field - The field name being edited</li>
58854              * <li>value - The value being set</li>
58855              * <li>originalValue - The original value for the field, before the edit.</li>
58856              * <li>row - The grid row index</li>
58857              * <li>column - The grid column index</li>
58858              * </ul>
58859              * @param {Object} e An edit event (see above for description)
58860              */
58861             "afteredit" : true,
58862             /**
58863              * @event validateedit
58864              * Fires after a cell is edited, but before the value is set in the record. 
58865          * You can use this to modify the value being set in the field, Return false
58866              * to cancel the change. The edit event object has the following properties <br />
58867              * <ul style="padding:5px;padding-left:16px;">
58868          * <li>editor - This editor</li>
58869              * <li>grid - This grid</li>
58870              * <li>record - The record being edited</li>
58871              * <li>field - The field name being edited</li>
58872              * <li>value - The value being set</li>
58873              * <li>originalValue - The original value for the field, before the edit.</li>
58874              * <li>row - The grid row index</li>
58875              * <li>column - The grid column index</li>
58876              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58877              * </ul>
58878              * @param {Object} e An edit event (see above for description)
58879              */
58880             "validateedit" : true
58881         });
58882     this.on("bodyscroll", this.stopEditing,  this);
58883     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58884 };
58885
58886 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58887     /**
58888      * @cfg {Number} clicksToEdit
58889      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58890      */
58891     clicksToEdit: 2,
58892
58893     // private
58894     isEditor : true,
58895     // private
58896     trackMouseOver: false, // causes very odd FF errors
58897
58898     onCellDblClick : function(g, row, col){
58899         this.startEditing(row, col);
58900     },
58901
58902     onEditComplete : function(ed, value, startValue){
58903         this.editing = false;
58904         this.activeEditor = null;
58905         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58906         var r = ed.record;
58907         var field = this.colModel.getDataIndex(ed.col);
58908         var e = {
58909             grid: this,
58910             record: r,
58911             field: field,
58912             originalValue: startValue,
58913             value: value,
58914             row: ed.row,
58915             column: ed.col,
58916             cancel:false,
58917             editor: ed
58918         };
58919         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58920         cell.show();
58921           
58922         if(String(value) !== String(startValue)){
58923             
58924             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58925                 r.set(field, e.value);
58926                 // if we are dealing with a combo box..
58927                 // then we also set the 'name' colum to be the displayField
58928                 if (ed.field.displayField && ed.field.name) {
58929                     r.set(ed.field.name, ed.field.el.dom.value);
58930                 }
58931                 
58932                 delete e.cancel; //?? why!!!
58933                 this.fireEvent("afteredit", e);
58934             }
58935         } else {
58936             this.fireEvent("afteredit", e); // always fire it!
58937         }
58938         this.view.focusCell(ed.row, ed.col);
58939     },
58940
58941     /**
58942      * Starts editing the specified for the specified row/column
58943      * @param {Number} rowIndex
58944      * @param {Number} colIndex
58945      */
58946     startEditing : function(row, col){
58947         this.stopEditing();
58948         if(this.colModel.isCellEditable(col, row)){
58949             this.view.ensureVisible(row, col, true);
58950           
58951             var r = this.dataSource.getAt(row);
58952             var field = this.colModel.getDataIndex(col);
58953             var cell = Roo.get(this.view.getCell(row,col));
58954             var e = {
58955                 grid: this,
58956                 record: r,
58957                 field: field,
58958                 value: r.data[field],
58959                 row: row,
58960                 column: col,
58961                 cancel:false 
58962             };
58963             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58964                 this.editing = true;
58965                 var ed = this.colModel.getCellEditor(col, row);
58966                 
58967                 if (!ed) {
58968                     return;
58969                 }
58970                 if(!ed.rendered){
58971                     ed.render(ed.parentEl || document.body);
58972                 }
58973                 ed.field.reset();
58974                
58975                 cell.hide();
58976                 
58977                 (function(){ // complex but required for focus issues in safari, ie and opera
58978                     ed.row = row;
58979                     ed.col = col;
58980                     ed.record = r;
58981                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58982                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58983                     this.activeEditor = ed;
58984                     var v = r.data[field];
58985                     ed.startEdit(this.view.getCell(row, col), v);
58986                     // combo's with 'displayField and name set
58987                     if (ed.field.displayField && ed.field.name) {
58988                         ed.field.el.dom.value = r.data[ed.field.name];
58989                     }
58990                     
58991                     
58992                 }).defer(50, this);
58993             }
58994         }
58995     },
58996         
58997     /**
58998      * Stops any active editing
58999      */
59000     stopEditing : function(){
59001         if(this.activeEditor){
59002             this.activeEditor.completeEdit();
59003         }
59004         this.activeEditor = null;
59005     },
59006         
59007          /**
59008      * Called to get grid's drag proxy text, by default returns this.ddText.
59009      * @return {String}
59010      */
59011     getDragDropText : function(){
59012         var count = this.selModel.getSelectedCell() ? 1 : 0;
59013         return String.format(this.ddText, count, count == 1 ? '' : 's');
59014     }
59015         
59016 });/*
59017  * Based on:
59018  * Ext JS Library 1.1.1
59019  * Copyright(c) 2006-2007, Ext JS, LLC.
59020  *
59021  * Originally Released Under LGPL - original licence link has changed is not relivant.
59022  *
59023  * Fork - LGPL
59024  * <script type="text/javascript">
59025  */
59026
59027 // private - not really -- you end up using it !
59028 // This is a support class used internally by the Grid components
59029
59030 /**
59031  * @class Roo.grid.GridEditor
59032  * @extends Roo.Editor
59033  * Class for creating and editable grid elements.
59034  * @param {Object} config any settings (must include field)
59035  */
59036 Roo.grid.GridEditor = function(field, config){
59037     if (!config && field.field) {
59038         config = field;
59039         field = Roo.factory(config.field, Roo.form);
59040     }
59041     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59042     field.monitorTab = false;
59043 };
59044
59045 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59046     
59047     /**
59048      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59049      */
59050     
59051     alignment: "tl-tl",
59052     autoSize: "width",
59053     hideEl : false,
59054     cls: "x-small-editor x-grid-editor",
59055     shim:false,
59056     shadow:"frame"
59057 });/*
59058  * Based on:
59059  * Ext JS Library 1.1.1
59060  * Copyright(c) 2006-2007, Ext JS, LLC.
59061  *
59062  * Originally Released Under LGPL - original licence link has changed is not relivant.
59063  *
59064  * Fork - LGPL
59065  * <script type="text/javascript">
59066  */
59067   
59068
59069   
59070 Roo.grid.PropertyRecord = Roo.data.Record.create([
59071     {name:'name',type:'string'},  'value'
59072 ]);
59073
59074
59075 Roo.grid.PropertyStore = function(grid, source){
59076     this.grid = grid;
59077     this.store = new Roo.data.Store({
59078         recordType : Roo.grid.PropertyRecord
59079     });
59080     this.store.on('update', this.onUpdate,  this);
59081     if(source){
59082         this.setSource(source);
59083     }
59084     Roo.grid.PropertyStore.superclass.constructor.call(this);
59085 };
59086
59087
59088
59089 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59090     setSource : function(o){
59091         this.source = o;
59092         this.store.removeAll();
59093         var data = [];
59094         for(var k in o){
59095             if(this.isEditableValue(o[k])){
59096                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59097             }
59098         }
59099         this.store.loadRecords({records: data}, {}, true);
59100     },
59101
59102     onUpdate : function(ds, record, type){
59103         if(type == Roo.data.Record.EDIT){
59104             var v = record.data['value'];
59105             var oldValue = record.modified['value'];
59106             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59107                 this.source[record.id] = v;
59108                 record.commit();
59109                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59110             }else{
59111                 record.reject();
59112             }
59113         }
59114     },
59115
59116     getProperty : function(row){
59117        return this.store.getAt(row);
59118     },
59119
59120     isEditableValue: function(val){
59121         if(val && val instanceof Date){
59122             return true;
59123         }else if(typeof val == 'object' || typeof val == 'function'){
59124             return false;
59125         }
59126         return true;
59127     },
59128
59129     setValue : function(prop, value){
59130         this.source[prop] = value;
59131         this.store.getById(prop).set('value', value);
59132     },
59133
59134     getSource : function(){
59135         return this.source;
59136     }
59137 });
59138
59139 Roo.grid.PropertyColumnModel = function(grid, store){
59140     this.grid = grid;
59141     var g = Roo.grid;
59142     g.PropertyColumnModel.superclass.constructor.call(this, [
59143         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59144         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59145     ]);
59146     this.store = store;
59147     this.bselect = Roo.DomHelper.append(document.body, {
59148         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59149             {tag: 'option', value: 'true', html: 'true'},
59150             {tag: 'option', value: 'false', html: 'false'}
59151         ]
59152     });
59153     Roo.id(this.bselect);
59154     var f = Roo.form;
59155     this.editors = {
59156         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59157         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59158         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59159         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59160         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59161     };
59162     this.renderCellDelegate = this.renderCell.createDelegate(this);
59163     this.renderPropDelegate = this.renderProp.createDelegate(this);
59164 };
59165
59166 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59167     
59168     
59169     nameText : 'Name',
59170     valueText : 'Value',
59171     
59172     dateFormat : 'm/j/Y',
59173     
59174     
59175     renderDate : function(dateVal){
59176         return dateVal.dateFormat(this.dateFormat);
59177     },
59178
59179     renderBool : function(bVal){
59180         return bVal ? 'true' : 'false';
59181     },
59182
59183     isCellEditable : function(colIndex, rowIndex){
59184         return colIndex == 1;
59185     },
59186
59187     getRenderer : function(col){
59188         return col == 1 ?
59189             this.renderCellDelegate : this.renderPropDelegate;
59190     },
59191
59192     renderProp : function(v){
59193         return this.getPropertyName(v);
59194     },
59195
59196     renderCell : function(val){
59197         var rv = val;
59198         if(val instanceof Date){
59199             rv = this.renderDate(val);
59200         }else if(typeof val == 'boolean'){
59201             rv = this.renderBool(val);
59202         }
59203         return Roo.util.Format.htmlEncode(rv);
59204     },
59205
59206     getPropertyName : function(name){
59207         var pn = this.grid.propertyNames;
59208         return pn && pn[name] ? pn[name] : name;
59209     },
59210
59211     getCellEditor : function(colIndex, rowIndex){
59212         var p = this.store.getProperty(rowIndex);
59213         var n = p.data['name'], val = p.data['value'];
59214         
59215         if(typeof(this.grid.customEditors[n]) == 'string'){
59216             return this.editors[this.grid.customEditors[n]];
59217         }
59218         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59219             return this.grid.customEditors[n];
59220         }
59221         if(val instanceof Date){
59222             return this.editors['date'];
59223         }else if(typeof val == 'number'){
59224             return this.editors['number'];
59225         }else if(typeof val == 'boolean'){
59226             return this.editors['boolean'];
59227         }else{
59228             return this.editors['string'];
59229         }
59230     }
59231 });
59232
59233 /**
59234  * @class Roo.grid.PropertyGrid
59235  * @extends Roo.grid.EditorGrid
59236  * This class represents the  interface of a component based property grid control.
59237  * <br><br>Usage:<pre><code>
59238  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59239       
59240  });
59241  // set any options
59242  grid.render();
59243  * </code></pre>
59244   
59245  * @constructor
59246  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59247  * The container MUST have some type of size defined for the grid to fill. The container will be
59248  * automatically set to position relative if it isn't already.
59249  * @param {Object} config A config object that sets properties on this grid.
59250  */
59251 Roo.grid.PropertyGrid = function(container, config){
59252     config = config || {};
59253     var store = new Roo.grid.PropertyStore(this);
59254     this.store = store;
59255     var cm = new Roo.grid.PropertyColumnModel(this, store);
59256     store.store.sort('name', 'ASC');
59257     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59258         ds: store.store,
59259         cm: cm,
59260         enableColLock:false,
59261         enableColumnMove:false,
59262         stripeRows:false,
59263         trackMouseOver: false,
59264         clicksToEdit:1
59265     }, config));
59266     this.getGridEl().addClass('x-props-grid');
59267     this.lastEditRow = null;
59268     this.on('columnresize', this.onColumnResize, this);
59269     this.addEvents({
59270          /**
59271              * @event beforepropertychange
59272              * Fires before a property changes (return false to stop?)
59273              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59274              * @param {String} id Record Id
59275              * @param {String} newval New Value
59276          * @param {String} oldval Old Value
59277              */
59278         "beforepropertychange": true,
59279         /**
59280              * @event propertychange
59281              * Fires after a property changes
59282              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59283              * @param {String} id Record Id
59284              * @param {String} newval New Value
59285          * @param {String} oldval Old Value
59286              */
59287         "propertychange": true
59288     });
59289     this.customEditors = this.customEditors || {};
59290 };
59291 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59292     
59293      /**
59294      * @cfg {Object} customEditors map of colnames=> custom editors.
59295      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59296      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59297      * false disables editing of the field.
59298          */
59299     
59300       /**
59301      * @cfg {Object} propertyNames map of property Names to their displayed value
59302          */
59303     
59304     render : function(){
59305         Roo.grid.PropertyGrid.superclass.render.call(this);
59306         this.autoSize.defer(100, this);
59307     },
59308
59309     autoSize : function(){
59310         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59311         if(this.view){
59312             this.view.fitColumns();
59313         }
59314     },
59315
59316     onColumnResize : function(){
59317         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59318         this.autoSize();
59319     },
59320     /**
59321      * Sets the data for the Grid
59322      * accepts a Key => Value object of all the elements avaiable.
59323      * @param {Object} data  to appear in grid.
59324      */
59325     setSource : function(source){
59326         this.store.setSource(source);
59327         //this.autoSize();
59328     },
59329     /**
59330      * Gets all the data from the grid.
59331      * @return {Object} data  data stored in grid
59332      */
59333     getSource : function(){
59334         return this.store.getSource();
59335     }
59336 });/*
59337   
59338  * Licence LGPL
59339  
59340  */
59341  
59342 /**
59343  * @class Roo.grid.Calendar
59344  * @extends Roo.util.Grid
59345  * This class extends the Grid to provide a calendar widget
59346  * <br><br>Usage:<pre><code>
59347  var grid = new Roo.grid.Calendar("my-container-id", {
59348      ds: myDataStore,
59349      cm: myColModel,
59350      selModel: mySelectionModel,
59351      autoSizeColumns: true,
59352      monitorWindowResize: false,
59353      trackMouseOver: true
59354      eventstore : real data store..
59355  });
59356  // set any options
59357  grid.render();
59358   
59359   * @constructor
59360  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59361  * The container MUST have some type of size defined for the grid to fill. The container will be
59362  * automatically set to position relative if it isn't already.
59363  * @param {Object} config A config object that sets properties on this grid.
59364  */
59365 Roo.grid.Calendar = function(container, config){
59366         // initialize the container
59367         this.container = Roo.get(container);
59368         this.container.update("");
59369         this.container.setStyle("overflow", "hidden");
59370     this.container.addClass('x-grid-container');
59371
59372     this.id = this.container.id;
59373
59374     Roo.apply(this, config);
59375     // check and correct shorthanded configs
59376     
59377     var rows = [];
59378     var d =1;
59379     for (var r = 0;r < 6;r++) {
59380         
59381         rows[r]=[];
59382         for (var c =0;c < 7;c++) {
59383             rows[r][c]= '';
59384         }
59385     }
59386     if (this.eventStore) {
59387         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59388         this.eventStore.on('load',this.onLoad, this);
59389         this.eventStore.on('beforeload',this.clearEvents, this);
59390          
59391     }
59392     
59393     this.dataSource = new Roo.data.Store({
59394             proxy: new Roo.data.MemoryProxy(rows),
59395             reader: new Roo.data.ArrayReader({}, [
59396                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59397     });
59398
59399     this.dataSource.load();
59400     this.ds = this.dataSource;
59401     this.ds.xmodule = this.xmodule || false;
59402     
59403     
59404     var cellRender = function(v,x,r)
59405     {
59406         return String.format(
59407             '<div class="fc-day  fc-widget-content"><div>' +
59408                 '<div class="fc-event-container"></div>' +
59409                 '<div class="fc-day-number">{0}</div>'+
59410                 
59411                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59412             '</div></div>', v);
59413     
59414     }
59415     
59416     
59417     this.colModel = new Roo.grid.ColumnModel( [
59418         {
59419             xtype: 'ColumnModel',
59420             xns: Roo.grid,
59421             dataIndex : 'weekday0',
59422             header : 'Sunday',
59423             renderer : cellRender
59424         },
59425         {
59426             xtype: 'ColumnModel',
59427             xns: Roo.grid,
59428             dataIndex : 'weekday1',
59429             header : 'Monday',
59430             renderer : cellRender
59431         },
59432         {
59433             xtype: 'ColumnModel',
59434             xns: Roo.grid,
59435             dataIndex : 'weekday2',
59436             header : 'Tuesday',
59437             renderer : cellRender
59438         },
59439         {
59440             xtype: 'ColumnModel',
59441             xns: Roo.grid,
59442             dataIndex : 'weekday3',
59443             header : 'Wednesday',
59444             renderer : cellRender
59445         },
59446         {
59447             xtype: 'ColumnModel',
59448             xns: Roo.grid,
59449             dataIndex : 'weekday4',
59450             header : 'Thursday',
59451             renderer : cellRender
59452         },
59453         {
59454             xtype: 'ColumnModel',
59455             xns: Roo.grid,
59456             dataIndex : 'weekday5',
59457             header : 'Friday',
59458             renderer : cellRender
59459         },
59460         {
59461             xtype: 'ColumnModel',
59462             xns: Roo.grid,
59463             dataIndex : 'weekday6',
59464             header : 'Saturday',
59465             renderer : cellRender
59466         }
59467     ]);
59468     this.cm = this.colModel;
59469     this.cm.xmodule = this.xmodule || false;
59470  
59471         
59472           
59473     //this.selModel = new Roo.grid.CellSelectionModel();
59474     //this.sm = this.selModel;
59475     //this.selModel.init(this);
59476     
59477     
59478     if(this.width){
59479         this.container.setWidth(this.width);
59480     }
59481
59482     if(this.height){
59483         this.container.setHeight(this.height);
59484     }
59485     /** @private */
59486         this.addEvents({
59487         // raw events
59488         /**
59489          * @event click
59490          * The raw click event for the entire grid.
59491          * @param {Roo.EventObject} e
59492          */
59493         "click" : true,
59494         /**
59495          * @event dblclick
59496          * The raw dblclick event for the entire grid.
59497          * @param {Roo.EventObject} e
59498          */
59499         "dblclick" : true,
59500         /**
59501          * @event contextmenu
59502          * The raw contextmenu event for the entire grid.
59503          * @param {Roo.EventObject} e
59504          */
59505         "contextmenu" : true,
59506         /**
59507          * @event mousedown
59508          * The raw mousedown event for the entire grid.
59509          * @param {Roo.EventObject} e
59510          */
59511         "mousedown" : true,
59512         /**
59513          * @event mouseup
59514          * The raw mouseup event for the entire grid.
59515          * @param {Roo.EventObject} e
59516          */
59517         "mouseup" : true,
59518         /**
59519          * @event mouseover
59520          * The raw mouseover event for the entire grid.
59521          * @param {Roo.EventObject} e
59522          */
59523         "mouseover" : true,
59524         /**
59525          * @event mouseout
59526          * The raw mouseout event for the entire grid.
59527          * @param {Roo.EventObject} e
59528          */
59529         "mouseout" : true,
59530         /**
59531          * @event keypress
59532          * The raw keypress event for the entire grid.
59533          * @param {Roo.EventObject} e
59534          */
59535         "keypress" : true,
59536         /**
59537          * @event keydown
59538          * The raw keydown event for the entire grid.
59539          * @param {Roo.EventObject} e
59540          */
59541         "keydown" : true,
59542
59543         // custom events
59544
59545         /**
59546          * @event cellclick
59547          * Fires when a cell is clicked
59548          * @param {Grid} this
59549          * @param {Number} rowIndex
59550          * @param {Number} columnIndex
59551          * @param {Roo.EventObject} e
59552          */
59553         "cellclick" : true,
59554         /**
59555          * @event celldblclick
59556          * Fires when a cell is double clicked
59557          * @param {Grid} this
59558          * @param {Number} rowIndex
59559          * @param {Number} columnIndex
59560          * @param {Roo.EventObject} e
59561          */
59562         "celldblclick" : true,
59563         /**
59564          * @event rowclick
59565          * Fires when a row is clicked
59566          * @param {Grid} this
59567          * @param {Number} rowIndex
59568          * @param {Roo.EventObject} e
59569          */
59570         "rowclick" : true,
59571         /**
59572          * @event rowdblclick
59573          * Fires when a row is double clicked
59574          * @param {Grid} this
59575          * @param {Number} rowIndex
59576          * @param {Roo.EventObject} e
59577          */
59578         "rowdblclick" : true,
59579         /**
59580          * @event headerclick
59581          * Fires when a header is clicked
59582          * @param {Grid} this
59583          * @param {Number} columnIndex
59584          * @param {Roo.EventObject} e
59585          */
59586         "headerclick" : true,
59587         /**
59588          * @event headerdblclick
59589          * Fires when a header cell is double clicked
59590          * @param {Grid} this
59591          * @param {Number} columnIndex
59592          * @param {Roo.EventObject} e
59593          */
59594         "headerdblclick" : true,
59595         /**
59596          * @event rowcontextmenu
59597          * Fires when a row is right clicked
59598          * @param {Grid} this
59599          * @param {Number} rowIndex
59600          * @param {Roo.EventObject} e
59601          */
59602         "rowcontextmenu" : true,
59603         /**
59604          * @event cellcontextmenu
59605          * Fires when a cell is right clicked
59606          * @param {Grid} this
59607          * @param {Number} rowIndex
59608          * @param {Number} cellIndex
59609          * @param {Roo.EventObject} e
59610          */
59611          "cellcontextmenu" : true,
59612         /**
59613          * @event headercontextmenu
59614          * Fires when a header is right clicked
59615          * @param {Grid} this
59616          * @param {Number} columnIndex
59617          * @param {Roo.EventObject} e
59618          */
59619         "headercontextmenu" : true,
59620         /**
59621          * @event bodyscroll
59622          * Fires when the body element is scrolled
59623          * @param {Number} scrollLeft
59624          * @param {Number} scrollTop
59625          */
59626         "bodyscroll" : true,
59627         /**
59628          * @event columnresize
59629          * Fires when the user resizes a column
59630          * @param {Number} columnIndex
59631          * @param {Number} newSize
59632          */
59633         "columnresize" : true,
59634         /**
59635          * @event columnmove
59636          * Fires when the user moves a column
59637          * @param {Number} oldIndex
59638          * @param {Number} newIndex
59639          */
59640         "columnmove" : true,
59641         /**
59642          * @event startdrag
59643          * Fires when row(s) start being dragged
59644          * @param {Grid} this
59645          * @param {Roo.GridDD} dd The drag drop object
59646          * @param {event} e The raw browser event
59647          */
59648         "startdrag" : true,
59649         /**
59650          * @event enddrag
59651          * Fires when a drag operation is complete
59652          * @param {Grid} this
59653          * @param {Roo.GridDD} dd The drag drop object
59654          * @param {event} e The raw browser event
59655          */
59656         "enddrag" : true,
59657         /**
59658          * @event dragdrop
59659          * Fires when dragged row(s) are dropped on a valid DD target
59660          * @param {Grid} this
59661          * @param {Roo.GridDD} dd The drag drop object
59662          * @param {String} targetId The target drag drop object
59663          * @param {event} e The raw browser event
59664          */
59665         "dragdrop" : true,
59666         /**
59667          * @event dragover
59668          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59669          * @param {Grid} this
59670          * @param {Roo.GridDD} dd The drag drop object
59671          * @param {String} targetId The target drag drop object
59672          * @param {event} e The raw browser event
59673          */
59674         "dragover" : true,
59675         /**
59676          * @event dragenter
59677          *  Fires when the dragged row(s) first cross another DD target while being dragged
59678          * @param {Grid} this
59679          * @param {Roo.GridDD} dd The drag drop object
59680          * @param {String} targetId The target drag drop object
59681          * @param {event} e The raw browser event
59682          */
59683         "dragenter" : true,
59684         /**
59685          * @event dragout
59686          * Fires when the dragged row(s) leave another DD target while being dragged
59687          * @param {Grid} this
59688          * @param {Roo.GridDD} dd The drag drop object
59689          * @param {String} targetId The target drag drop object
59690          * @param {event} e The raw browser event
59691          */
59692         "dragout" : true,
59693         /**
59694          * @event rowclass
59695          * Fires when a row is rendered, so you can change add a style to it.
59696          * @param {GridView} gridview   The grid view
59697          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59698          */
59699         'rowclass' : true,
59700
59701         /**
59702          * @event render
59703          * Fires when the grid is rendered
59704          * @param {Grid} grid
59705          */
59706         'render' : true,
59707             /**
59708              * @event select
59709              * Fires when a date is selected
59710              * @param {DatePicker} this
59711              * @param {Date} date The selected date
59712              */
59713         'select': true,
59714         /**
59715              * @event monthchange
59716              * Fires when the displayed month changes 
59717              * @param {DatePicker} this
59718              * @param {Date} date The selected month
59719              */
59720         'monthchange': true,
59721         /**
59722              * @event evententer
59723              * Fires when mouse over an event
59724              * @param {Calendar} this
59725              * @param {event} Event
59726              */
59727         'evententer': true,
59728         /**
59729              * @event eventleave
59730              * Fires when the mouse leaves an
59731              * @param {Calendar} this
59732              * @param {event}
59733              */
59734         'eventleave': true,
59735         /**
59736              * @event eventclick
59737              * Fires when the mouse click an
59738              * @param {Calendar} this
59739              * @param {event}
59740              */
59741         'eventclick': true,
59742         /**
59743              * @event eventrender
59744              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59745              * @param {Calendar} this
59746              * @param {data} data to be modified
59747              */
59748         'eventrender': true
59749         
59750     });
59751
59752     Roo.grid.Grid.superclass.constructor.call(this);
59753     this.on('render', function() {
59754         this.view.el.addClass('x-grid-cal'); 
59755         
59756         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59757
59758     },this);
59759     
59760     if (!Roo.grid.Calendar.style) {
59761         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59762             
59763             
59764             '.x-grid-cal .x-grid-col' :  {
59765                 height: 'auto !important',
59766                 'vertical-align': 'top'
59767             },
59768             '.x-grid-cal  .fc-event-hori' : {
59769                 height: '14px'
59770             }
59771              
59772             
59773         }, Roo.id());
59774     }
59775
59776     
59777     
59778 };
59779 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59780     /**
59781      * @cfg {Store} eventStore The store that loads events.
59782      */
59783     eventStore : 25,
59784
59785      
59786     activeDate : false,
59787     startDay : 0,
59788     autoWidth : true,
59789     monitorWindowResize : false,
59790
59791     
59792     resizeColumns : function() {
59793         var col = (this.view.el.getWidth() / 7) - 3;
59794         // loop through cols, and setWidth
59795         for(var i =0 ; i < 7 ; i++){
59796             this.cm.setColumnWidth(i, col);
59797         }
59798     },
59799      setDate :function(date) {
59800         
59801         Roo.log('setDate?');
59802         
59803         this.resizeColumns();
59804         var vd = this.activeDate;
59805         this.activeDate = date;
59806 //        if(vd && this.el){
59807 //            var t = date.getTime();
59808 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59809 //                Roo.log('using add remove');
59810 //                
59811 //                this.fireEvent('monthchange', this, date);
59812 //                
59813 //                this.cells.removeClass("fc-state-highlight");
59814 //                this.cells.each(function(c){
59815 //                   if(c.dateValue == t){
59816 //                       c.addClass("fc-state-highlight");
59817 //                       setTimeout(function(){
59818 //                            try{c.dom.firstChild.focus();}catch(e){}
59819 //                       }, 50);
59820 //                       return false;
59821 //                   }
59822 //                   return true;
59823 //                });
59824 //                return;
59825 //            }
59826 //        }
59827         
59828         var days = date.getDaysInMonth();
59829         
59830         var firstOfMonth = date.getFirstDateOfMonth();
59831         var startingPos = firstOfMonth.getDay()-this.startDay;
59832         
59833         if(startingPos < this.startDay){
59834             startingPos += 7;
59835         }
59836         
59837         var pm = date.add(Date.MONTH, -1);
59838         var prevStart = pm.getDaysInMonth()-startingPos;
59839 //        
59840         
59841         
59842         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59843         
59844         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59845         //this.cells.addClassOnOver('fc-state-hover');
59846         
59847         var cells = this.cells.elements;
59848         var textEls = this.textNodes;
59849         
59850         //Roo.each(cells, function(cell){
59851         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59852         //});
59853         
59854         days += startingPos;
59855
59856         // convert everything to numbers so it's fast
59857         var day = 86400000;
59858         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59859         //Roo.log(d);
59860         //Roo.log(pm);
59861         //Roo.log(prevStart);
59862         
59863         var today = new Date().clearTime().getTime();
59864         var sel = date.clearTime().getTime();
59865         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59866         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59867         var ddMatch = this.disabledDatesRE;
59868         var ddText = this.disabledDatesText;
59869         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59870         var ddaysText = this.disabledDaysText;
59871         var format = this.format;
59872         
59873         var setCellClass = function(cal, cell){
59874             
59875             //Roo.log('set Cell Class');
59876             cell.title = "";
59877             var t = d.getTime();
59878             
59879             //Roo.log(d);
59880             
59881             
59882             cell.dateValue = t;
59883             if(t == today){
59884                 cell.className += " fc-today";
59885                 cell.className += " fc-state-highlight";
59886                 cell.title = cal.todayText;
59887             }
59888             if(t == sel){
59889                 // disable highlight in other month..
59890                 cell.className += " fc-state-highlight";
59891                 
59892             }
59893             // disabling
59894             if(t < min) {
59895                 //cell.className = " fc-state-disabled";
59896                 cell.title = cal.minText;
59897                 return;
59898             }
59899             if(t > max) {
59900                 //cell.className = " fc-state-disabled";
59901                 cell.title = cal.maxText;
59902                 return;
59903             }
59904             if(ddays){
59905                 if(ddays.indexOf(d.getDay()) != -1){
59906                     // cell.title = ddaysText;
59907                    // cell.className = " fc-state-disabled";
59908                 }
59909             }
59910             if(ddMatch && format){
59911                 var fvalue = d.dateFormat(format);
59912                 if(ddMatch.test(fvalue)){
59913                     cell.title = ddText.replace("%0", fvalue);
59914                    cell.className = " fc-state-disabled";
59915                 }
59916             }
59917             
59918             if (!cell.initialClassName) {
59919                 cell.initialClassName = cell.dom.className;
59920             }
59921             
59922             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59923         };
59924
59925         var i = 0;
59926         
59927         for(; i < startingPos; i++) {
59928             cells[i].dayName =  (++prevStart);
59929             Roo.log(textEls[i]);
59930             d.setDate(d.getDate()+1);
59931             
59932             //cells[i].className = "fc-past fc-other-month";
59933             setCellClass(this, cells[i]);
59934         }
59935         
59936         var intDay = 0;
59937         
59938         for(; i < days; i++){
59939             intDay = i - startingPos + 1;
59940             cells[i].dayName =  (intDay);
59941             d.setDate(d.getDate()+1);
59942             
59943             cells[i].className = ''; // "x-date-active";
59944             setCellClass(this, cells[i]);
59945         }
59946         var extraDays = 0;
59947         
59948         for(; i < 42; i++) {
59949             //textEls[i].innerHTML = (++extraDays);
59950             
59951             d.setDate(d.getDate()+1);
59952             cells[i].dayName = (++extraDays);
59953             cells[i].className = "fc-future fc-other-month";
59954             setCellClass(this, cells[i]);
59955         }
59956         
59957         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59958         
59959         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59960         
59961         // this will cause all the cells to mis
59962         var rows= [];
59963         var i =0;
59964         for (var r = 0;r < 6;r++) {
59965             for (var c =0;c < 7;c++) {
59966                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59967             }    
59968         }
59969         
59970         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59971         for(i=0;i<cells.length;i++) {
59972             
59973             this.cells.elements[i].dayName = cells[i].dayName ;
59974             this.cells.elements[i].className = cells[i].className;
59975             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59976             this.cells.elements[i].title = cells[i].title ;
59977             this.cells.elements[i].dateValue = cells[i].dateValue ;
59978         }
59979         
59980         
59981         
59982         
59983         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59984         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59985         
59986         ////if(totalRows != 6){
59987             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59988            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59989        // }
59990         
59991         this.fireEvent('monthchange', this, date);
59992         
59993         
59994     },
59995  /**
59996      * Returns the grid's SelectionModel.
59997      * @return {SelectionModel}
59998      */
59999     getSelectionModel : function(){
60000         if(!this.selModel){
60001             this.selModel = new Roo.grid.CellSelectionModel();
60002         }
60003         return this.selModel;
60004     },
60005
60006     load: function() {
60007         this.eventStore.load()
60008         
60009         
60010         
60011     },
60012     
60013     findCell : function(dt) {
60014         dt = dt.clearTime().getTime();
60015         var ret = false;
60016         this.cells.each(function(c){
60017             //Roo.log("check " +c.dateValue + '?=' + dt);
60018             if(c.dateValue == dt){
60019                 ret = c;
60020                 return false;
60021             }
60022             return true;
60023         });
60024         
60025         return ret;
60026     },
60027     
60028     findCells : function(rec) {
60029         var s = rec.data.start_dt.clone().clearTime().getTime();
60030        // Roo.log(s);
60031         var e= rec.data.end_dt.clone().clearTime().getTime();
60032        // Roo.log(e);
60033         var ret = [];
60034         this.cells.each(function(c){
60035              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60036             
60037             if(c.dateValue > e){
60038                 return ;
60039             }
60040             if(c.dateValue < s){
60041                 return ;
60042             }
60043             ret.push(c);
60044         });
60045         
60046         return ret;    
60047     },
60048     
60049     findBestRow: function(cells)
60050     {
60051         var ret = 0;
60052         
60053         for (var i =0 ; i < cells.length;i++) {
60054             ret  = Math.max(cells[i].rows || 0,ret);
60055         }
60056         return ret;
60057         
60058     },
60059     
60060     
60061     addItem : function(rec)
60062     {
60063         // look for vertical location slot in
60064         var cells = this.findCells(rec);
60065         
60066         rec.row = this.findBestRow(cells);
60067         
60068         // work out the location.
60069         
60070         var crow = false;
60071         var rows = [];
60072         for(var i =0; i < cells.length; i++) {
60073             if (!crow) {
60074                 crow = {
60075                     start : cells[i],
60076                     end :  cells[i]
60077                 };
60078                 continue;
60079             }
60080             if (crow.start.getY() == cells[i].getY()) {
60081                 // on same row.
60082                 crow.end = cells[i];
60083                 continue;
60084             }
60085             // different row.
60086             rows.push(crow);
60087             crow = {
60088                 start: cells[i],
60089                 end : cells[i]
60090             };
60091             
60092         }
60093         
60094         rows.push(crow);
60095         rec.els = [];
60096         rec.rows = rows;
60097         rec.cells = cells;
60098         for (var i = 0; i < cells.length;i++) {
60099             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60100             
60101         }
60102         
60103         
60104     },
60105     
60106     clearEvents: function() {
60107         
60108         if (!this.eventStore.getCount()) {
60109             return;
60110         }
60111         // reset number of rows in cells.
60112         Roo.each(this.cells.elements, function(c){
60113             c.rows = 0;
60114         });
60115         
60116         this.eventStore.each(function(e) {
60117             this.clearEvent(e);
60118         },this);
60119         
60120     },
60121     
60122     clearEvent : function(ev)
60123     {
60124         if (ev.els) {
60125             Roo.each(ev.els, function(el) {
60126                 el.un('mouseenter' ,this.onEventEnter, this);
60127                 el.un('mouseleave' ,this.onEventLeave, this);
60128                 el.remove();
60129             },this);
60130             ev.els = [];
60131         }
60132     },
60133     
60134     
60135     renderEvent : function(ev,ctr) {
60136         if (!ctr) {
60137              ctr = this.view.el.select('.fc-event-container',true).first();
60138         }
60139         
60140          
60141         this.clearEvent(ev);
60142             //code
60143        
60144         
60145         
60146         ev.els = [];
60147         var cells = ev.cells;
60148         var rows = ev.rows;
60149         this.fireEvent('eventrender', this, ev);
60150         
60151         for(var i =0; i < rows.length; i++) {
60152             
60153             cls = '';
60154             if (i == 0) {
60155                 cls += ' fc-event-start';
60156             }
60157             if ((i+1) == rows.length) {
60158                 cls += ' fc-event-end';
60159             }
60160             
60161             //Roo.log(ev.data);
60162             // how many rows should it span..
60163             var cg = this.eventTmpl.append(ctr,Roo.apply({
60164                 fccls : cls
60165                 
60166             }, ev.data) , true);
60167             
60168             
60169             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60170             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60171             cg.on('click', this.onEventClick, this, ev);
60172             
60173             ev.els.push(cg);
60174             
60175             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60176             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60177             //Roo.log(cg);
60178              
60179             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60180             cg.setWidth(ebox.right - sbox.x -2);
60181         }
60182     },
60183     
60184     renderEvents: function()
60185     {   
60186         // first make sure there is enough space..
60187         
60188         if (!this.eventTmpl) {
60189             this.eventTmpl = new Roo.Template(
60190                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60191                     '<div class="fc-event-inner">' +
60192                         '<span class="fc-event-time">{time}</span>' +
60193                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60194                     '</div>' +
60195                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60196                 '</div>'
60197             );
60198                 
60199         }
60200                
60201         
60202         
60203         this.cells.each(function(c) {
60204             //Roo.log(c.select('.fc-day-content div',true).first());
60205             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60206         });
60207         
60208         var ctr = this.view.el.select('.fc-event-container',true).first();
60209         
60210         var cls;
60211         this.eventStore.each(function(ev){
60212             
60213             this.renderEvent(ev);
60214              
60215              
60216         }, this);
60217         this.view.layout();
60218         
60219     },
60220     
60221     onEventEnter: function (e, el,event,d) {
60222         this.fireEvent('evententer', this, el, event);
60223     },
60224     
60225     onEventLeave: function (e, el,event,d) {
60226         this.fireEvent('eventleave', this, el, event);
60227     },
60228     
60229     onEventClick: function (e, el,event,d) {
60230         this.fireEvent('eventclick', this, el, event);
60231     },
60232     
60233     onMonthChange: function () {
60234         this.store.load();
60235     },
60236     
60237     onLoad: function () {
60238         
60239         //Roo.log('calendar onload');
60240 //         
60241         if(this.eventStore.getCount() > 0){
60242             
60243            
60244             
60245             this.eventStore.each(function(d){
60246                 
60247                 
60248                 // FIXME..
60249                 var add =   d.data;
60250                 if (typeof(add.end_dt) == 'undefined')  {
60251                     Roo.log("Missing End time in calendar data: ");
60252                     Roo.log(d);
60253                     return;
60254                 }
60255                 if (typeof(add.start_dt) == 'undefined')  {
60256                     Roo.log("Missing Start time in calendar data: ");
60257                     Roo.log(d);
60258                     return;
60259                 }
60260                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60261                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60262                 add.id = add.id || d.id;
60263                 add.title = add.title || '??';
60264                 
60265                 this.addItem(d);
60266                 
60267              
60268             },this);
60269         }
60270         
60271         this.renderEvents();
60272     }
60273     
60274
60275 });
60276 /*
60277  grid : {
60278                 xtype: 'Grid',
60279                 xns: Roo.grid,
60280                 listeners : {
60281                     render : function ()
60282                     {
60283                         _this.grid = this;
60284                         
60285                         if (!this.view.el.hasClass('course-timesheet')) {
60286                             this.view.el.addClass('course-timesheet');
60287                         }
60288                         if (this.tsStyle) {
60289                             this.ds.load({});
60290                             return; 
60291                         }
60292                         Roo.log('width');
60293                         Roo.log(_this.grid.view.el.getWidth());
60294                         
60295                         
60296                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60297                             '.course-timesheet .x-grid-row' : {
60298                                 height: '80px'
60299                             },
60300                             '.x-grid-row td' : {
60301                                 'vertical-align' : 0
60302                             },
60303                             '.course-edit-link' : {
60304                                 'color' : 'blue',
60305                                 'text-overflow' : 'ellipsis',
60306                                 'overflow' : 'hidden',
60307                                 'white-space' : 'nowrap',
60308                                 'cursor' : 'pointer'
60309                             },
60310                             '.sub-link' : {
60311                                 'color' : 'green'
60312                             },
60313                             '.de-act-sup-link' : {
60314                                 'color' : 'purple',
60315                                 'text-decoration' : 'line-through'
60316                             },
60317                             '.de-act-link' : {
60318                                 'color' : 'red',
60319                                 'text-decoration' : 'line-through'
60320                             },
60321                             '.course-timesheet .course-highlight' : {
60322                                 'border-top-style': 'dashed !important',
60323                                 'border-bottom-bottom': 'dashed !important'
60324                             },
60325                             '.course-timesheet .course-item' : {
60326                                 'font-family'   : 'tahoma, arial, helvetica',
60327                                 'font-size'     : '11px',
60328                                 'overflow'      : 'hidden',
60329                                 'padding-left'  : '10px',
60330                                 'padding-right' : '10px',
60331                                 'padding-top' : '10px' 
60332                             }
60333                             
60334                         }, Roo.id());
60335                                 this.ds.load({});
60336                     }
60337                 },
60338                 autoWidth : true,
60339                 monitorWindowResize : false,
60340                 cellrenderer : function(v,x,r)
60341                 {
60342                     return v;
60343                 },
60344                 sm : {
60345                     xtype: 'CellSelectionModel',
60346                     xns: Roo.grid
60347                 },
60348                 dataSource : {
60349                     xtype: 'Store',
60350                     xns: Roo.data,
60351                     listeners : {
60352                         beforeload : function (_self, options)
60353                         {
60354                             options.params = options.params || {};
60355                             options.params._month = _this.monthField.getValue();
60356                             options.params.limit = 9999;
60357                             options.params['sort'] = 'when_dt';    
60358                             options.params['dir'] = 'ASC';    
60359                             this.proxy.loadResponse = this.loadResponse;
60360                             Roo.log("load?");
60361                             //this.addColumns();
60362                         },
60363                         load : function (_self, records, options)
60364                         {
60365                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60366                                 // if you click on the translation.. you can edit it...
60367                                 var el = Roo.get(this);
60368                                 var id = el.dom.getAttribute('data-id');
60369                                 var d = el.dom.getAttribute('data-date');
60370                                 var t = el.dom.getAttribute('data-time');
60371                                 //var id = this.child('span').dom.textContent;
60372                                 
60373                                 //Roo.log(this);
60374                                 Pman.Dialog.CourseCalendar.show({
60375                                     id : id,
60376                                     when_d : d,
60377                                     when_t : t,
60378                                     productitem_active : id ? 1 : 0
60379                                 }, function() {
60380                                     _this.grid.ds.load({});
60381                                 });
60382                            
60383                            });
60384                            
60385                            _this.panel.fireEvent('resize', [ '', '' ]);
60386                         }
60387                     },
60388                     loadResponse : function(o, success, response){
60389                             // this is overridden on before load..
60390                             
60391                             Roo.log("our code?");       
60392                             //Roo.log(success);
60393                             //Roo.log(response)
60394                             delete this.activeRequest;
60395                             if(!success){
60396                                 this.fireEvent("loadexception", this, o, response);
60397                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60398                                 return;
60399                             }
60400                             var result;
60401                             try {
60402                                 result = o.reader.read(response);
60403                             }catch(e){
60404                                 Roo.log("load exception?");
60405                                 this.fireEvent("loadexception", this, o, response, e);
60406                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60407                                 return;
60408                             }
60409                             Roo.log("ready...");        
60410                             // loop through result.records;
60411                             // and set this.tdate[date] = [] << array of records..
60412                             _this.tdata  = {};
60413                             Roo.each(result.records, function(r){
60414                                 //Roo.log(r.data);
60415                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60416                                     _this.tdata[r.data.when_dt.format('j')] = [];
60417                                 }
60418                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60419                             });
60420                             
60421                             //Roo.log(_this.tdata);
60422                             
60423                             result.records = [];
60424                             result.totalRecords = 6;
60425                     
60426                             // let's generate some duumy records for the rows.
60427                             //var st = _this.dateField.getValue();
60428                             
60429                             // work out monday..
60430                             //st = st.add(Date.DAY, -1 * st.format('w'));
60431                             
60432                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60433                             
60434                             var firstOfMonth = date.getFirstDayOfMonth();
60435                             var days = date.getDaysInMonth();
60436                             var d = 1;
60437                             var firstAdded = false;
60438                             for (var i = 0; i < result.totalRecords ; i++) {
60439                                 //var d= st.add(Date.DAY, i);
60440                                 var row = {};
60441                                 var added = 0;
60442                                 for(var w = 0 ; w < 7 ; w++){
60443                                     if(!firstAdded && firstOfMonth != w){
60444                                         continue;
60445                                     }
60446                                     if(d > days){
60447                                         continue;
60448                                     }
60449                                     firstAdded = true;
60450                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60451                                     row['weekday'+w] = String.format(
60452                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60453                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60454                                                     d,
60455                                                     date.format('Y-m-')+dd
60456                                                 );
60457                                     added++;
60458                                     if(typeof(_this.tdata[d]) != 'undefined'){
60459                                         Roo.each(_this.tdata[d], function(r){
60460                                             var is_sub = '';
60461                                             var deactive = '';
60462                                             var id = r.id;
60463                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60464                                             if(r.parent_id*1>0){
60465                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60466                                                 id = r.parent_id;
60467                                             }
60468                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60469                                                 deactive = 'de-act-link';
60470                                             }
60471                                             
60472                                             row['weekday'+w] += String.format(
60473                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60474                                                     id, //0
60475                                                     r.product_id_name, //1
60476                                                     r.when_dt.format('h:ia'), //2
60477                                                     is_sub, //3
60478                                                     deactive, //4
60479                                                     desc // 5
60480                                             );
60481                                         });
60482                                     }
60483                                     d++;
60484                                 }
60485                                 
60486                                 // only do this if something added..
60487                                 if(added > 0){ 
60488                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60489                                 }
60490                                 
60491                                 
60492                                 // push it twice. (second one with an hour..
60493                                 
60494                             }
60495                             //Roo.log(result);
60496                             this.fireEvent("load", this, o, o.request.arg);
60497                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60498                         },
60499                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60500                     proxy : {
60501                         xtype: 'HttpProxy',
60502                         xns: Roo.data,
60503                         method : 'GET',
60504                         url : baseURL + '/Roo/Shop_course.php'
60505                     },
60506                     reader : {
60507                         xtype: 'JsonReader',
60508                         xns: Roo.data,
60509                         id : 'id',
60510                         fields : [
60511                             {
60512                                 'name': 'id',
60513                                 'type': 'int'
60514                             },
60515                             {
60516                                 'name': 'when_dt',
60517                                 'type': 'string'
60518                             },
60519                             {
60520                                 'name': 'end_dt',
60521                                 'type': 'string'
60522                             },
60523                             {
60524                                 'name': 'parent_id',
60525                                 'type': 'int'
60526                             },
60527                             {
60528                                 'name': 'product_id',
60529                                 'type': 'int'
60530                             },
60531                             {
60532                                 'name': 'productitem_id',
60533                                 'type': 'int'
60534                             },
60535                             {
60536                                 'name': 'guid',
60537                                 'type': 'int'
60538                             }
60539                         ]
60540                     }
60541                 },
60542                 toolbar : {
60543                     xtype: 'Toolbar',
60544                     xns: Roo,
60545                     items : [
60546                         {
60547                             xtype: 'Button',
60548                             xns: Roo.Toolbar,
60549                             listeners : {
60550                                 click : function (_self, e)
60551                                 {
60552                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60553                                     sd.setMonth(sd.getMonth()-1);
60554                                     _this.monthField.setValue(sd.format('Y-m-d'));
60555                                     _this.grid.ds.load({});
60556                                 }
60557                             },
60558                             text : "Back"
60559                         },
60560                         {
60561                             xtype: 'Separator',
60562                             xns: Roo.Toolbar
60563                         },
60564                         {
60565                             xtype: 'MonthField',
60566                             xns: Roo.form,
60567                             listeners : {
60568                                 render : function (_self)
60569                                 {
60570                                     _this.monthField = _self;
60571                                    // _this.monthField.set  today
60572                                 },
60573                                 select : function (combo, date)
60574                                 {
60575                                     _this.grid.ds.load({});
60576                                 }
60577                             },
60578                             value : (function() { return new Date(); })()
60579                         },
60580                         {
60581                             xtype: 'Separator',
60582                             xns: Roo.Toolbar
60583                         },
60584                         {
60585                             xtype: 'TextItem',
60586                             xns: Roo.Toolbar,
60587                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60588                         },
60589                         {
60590                             xtype: 'Fill',
60591                             xns: Roo.Toolbar
60592                         },
60593                         {
60594                             xtype: 'Button',
60595                             xns: Roo.Toolbar,
60596                             listeners : {
60597                                 click : function (_self, e)
60598                                 {
60599                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60600                                     sd.setMonth(sd.getMonth()+1);
60601                                     _this.monthField.setValue(sd.format('Y-m-d'));
60602                                     _this.grid.ds.load({});
60603                                 }
60604                             },
60605                             text : "Next"
60606                         }
60607                     ]
60608                 },
60609                  
60610             }
60611         };
60612         
60613         *//*
60614  * Based on:
60615  * Ext JS Library 1.1.1
60616  * Copyright(c) 2006-2007, Ext JS, LLC.
60617  *
60618  * Originally Released Under LGPL - original licence link has changed is not relivant.
60619  *
60620  * Fork - LGPL
60621  * <script type="text/javascript">
60622  */
60623  
60624 /**
60625  * @class Roo.LoadMask
60626  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60627  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60628  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60629  * element's UpdateManager load indicator and will be destroyed after the initial load.
60630  * @constructor
60631  * Create a new LoadMask
60632  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60633  * @param {Object} config The config object
60634  */
60635 Roo.LoadMask = function(el, config){
60636     this.el = Roo.get(el);
60637     Roo.apply(this, config);
60638     if(this.store){
60639         this.store.on('beforeload', this.onBeforeLoad, this);
60640         this.store.on('load', this.onLoad, this);
60641         this.store.on('loadexception', this.onLoadException, this);
60642         this.removeMask = false;
60643     }else{
60644         var um = this.el.getUpdateManager();
60645         um.showLoadIndicator = false; // disable the default indicator
60646         um.on('beforeupdate', this.onBeforeLoad, this);
60647         um.on('update', this.onLoad, this);
60648         um.on('failure', this.onLoad, this);
60649         this.removeMask = true;
60650     }
60651 };
60652
60653 Roo.LoadMask.prototype = {
60654     /**
60655      * @cfg {Boolean} removeMask
60656      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60657      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60658      */
60659     /**
60660      * @cfg {String} msg
60661      * The text to display in a centered loading message box (defaults to 'Loading...')
60662      */
60663     msg : 'Loading...',
60664     /**
60665      * @cfg {String} msgCls
60666      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60667      */
60668     msgCls : 'x-mask-loading',
60669
60670     /**
60671      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60672      * @type Boolean
60673      */
60674     disabled: false,
60675
60676     /**
60677      * Disables the mask to prevent it from being displayed
60678      */
60679     disable : function(){
60680        this.disabled = true;
60681     },
60682
60683     /**
60684      * Enables the mask so that it can be displayed
60685      */
60686     enable : function(){
60687         this.disabled = false;
60688     },
60689     
60690     onLoadException : function()
60691     {
60692         Roo.log(arguments);
60693         
60694         if (typeof(arguments[3]) != 'undefined') {
60695             Roo.MessageBox.alert("Error loading",arguments[3]);
60696         } 
60697         /*
60698         try {
60699             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60700                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60701             }   
60702         } catch(e) {
60703             
60704         }
60705         */
60706     
60707         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60708     },
60709     // private
60710     onLoad : function()
60711     {
60712         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60713     },
60714
60715     // private
60716     onBeforeLoad : function(){
60717         if(!this.disabled){
60718             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60719         }
60720     },
60721
60722     // private
60723     destroy : function(){
60724         if(this.store){
60725             this.store.un('beforeload', this.onBeforeLoad, this);
60726             this.store.un('load', this.onLoad, this);
60727             this.store.un('loadexception', this.onLoadException, this);
60728         }else{
60729             var um = this.el.getUpdateManager();
60730             um.un('beforeupdate', this.onBeforeLoad, this);
60731             um.un('update', this.onLoad, this);
60732             um.un('failure', this.onLoad, this);
60733         }
60734     }
60735 };/*
60736  * Based on:
60737  * Ext JS Library 1.1.1
60738  * Copyright(c) 2006-2007, Ext JS, LLC.
60739  *
60740  * Originally Released Under LGPL - original licence link has changed is not relivant.
60741  *
60742  * Fork - LGPL
60743  * <script type="text/javascript">
60744  */
60745
60746
60747 /**
60748  * @class Roo.XTemplate
60749  * @extends Roo.Template
60750  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60751 <pre><code>
60752 var t = new Roo.XTemplate(
60753         '&lt;select name="{name}"&gt;',
60754                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60755         '&lt;/select&gt;'
60756 );
60757  
60758 // then append, applying the master template values
60759  </code></pre>
60760  *
60761  * Supported features:
60762  *
60763  *  Tags:
60764
60765 <pre><code>
60766       {a_variable} - output encoded.
60767       {a_variable.format:("Y-m-d")} - call a method on the variable
60768       {a_variable:raw} - unencoded output
60769       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60770       {a_variable:this.method_on_template(...)} - call a method on the template object.
60771  
60772 </code></pre>
60773  *  The tpl tag:
60774 <pre><code>
60775         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60776         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60777         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60778         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60779   
60780         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60781         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60782 </code></pre>
60783  *      
60784  */
60785 Roo.XTemplate = function()
60786 {
60787     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60788     if (this.html) {
60789         this.compile();
60790     }
60791 };
60792
60793
60794 Roo.extend(Roo.XTemplate, Roo.Template, {
60795
60796     /**
60797      * The various sub templates
60798      */
60799     tpls : false,
60800     /**
60801      *
60802      * basic tag replacing syntax
60803      * WORD:WORD()
60804      *
60805      * // you can fake an object call by doing this
60806      *  x.t:(test,tesT) 
60807      * 
60808      */
60809     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60810
60811     /**
60812      * compile the template
60813      *
60814      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60815      *
60816      */
60817     compile: function()
60818     {
60819         var s = this.html;
60820      
60821         s = ['<tpl>', s, '</tpl>'].join('');
60822     
60823         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60824             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60825             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60826             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60827             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60828             m,
60829             id     = 0,
60830             tpls   = [];
60831     
60832         while(true == !!(m = s.match(re))){
60833             var forMatch   = m[0].match(nameRe),
60834                 ifMatch   = m[0].match(ifRe),
60835                 execMatch   = m[0].match(execRe),
60836                 namedMatch   = m[0].match(namedRe),
60837                 
60838                 exp  = null, 
60839                 fn   = null,
60840                 exec = null,
60841                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60842                 
60843             if (ifMatch) {
60844                 // if - puts fn into test..
60845                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60846                 if(exp){
60847                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60848                 }
60849             }
60850             
60851             if (execMatch) {
60852                 // exec - calls a function... returns empty if true is  returned.
60853                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60854                 if(exp){
60855                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60856                 }
60857             }
60858             
60859             
60860             if (name) {
60861                 // for = 
60862                 switch(name){
60863                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60864                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60865                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60866                 }
60867             }
60868             var uid = namedMatch ? namedMatch[1] : id;
60869             
60870             
60871             tpls.push({
60872                 id:     namedMatch ? namedMatch[1] : id,
60873                 target: name,
60874                 exec:   exec,
60875                 test:   fn,
60876                 body:   m[1] || ''
60877             });
60878             if (namedMatch) {
60879                 s = s.replace(m[0], '');
60880             } else { 
60881                 s = s.replace(m[0], '{xtpl'+ id + '}');
60882             }
60883             ++id;
60884         }
60885         this.tpls = [];
60886         for(var i = tpls.length-1; i >= 0; --i){
60887             this.compileTpl(tpls[i]);
60888             this.tpls[tpls[i].id] = tpls[i];
60889         }
60890         this.master = tpls[tpls.length-1];
60891         return this;
60892     },
60893     /**
60894      * same as applyTemplate, except it's done to one of the subTemplates
60895      * when using named templates, you can do:
60896      *
60897      * var str = pl.applySubTemplate('your-name', values);
60898      *
60899      * 
60900      * @param {Number} id of the template
60901      * @param {Object} values to apply to template
60902      * @param {Object} parent (normaly the instance of this object)
60903      */
60904     applySubTemplate : function(id, values, parent)
60905     {
60906         
60907         
60908         var t = this.tpls[id];
60909         
60910         
60911         try { 
60912             if(t.test && !t.test.call(this, values, parent)){
60913                 return '';
60914             }
60915         } catch(e) {
60916             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60917             Roo.log(e.toString());
60918             Roo.log(t.test);
60919             return ''
60920         }
60921         try { 
60922             
60923             if(t.exec && t.exec.call(this, values, parent)){
60924                 return '';
60925             }
60926         } catch(e) {
60927             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60928             Roo.log(e.toString());
60929             Roo.log(t.exec);
60930             return ''
60931         }
60932         try {
60933             var vs = t.target ? t.target.call(this, values, parent) : values;
60934             parent = t.target ? values : parent;
60935             if(t.target && vs instanceof Array){
60936                 var buf = [];
60937                 for(var i = 0, len = vs.length; i < len; i++){
60938                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60939                 }
60940                 return buf.join('');
60941             }
60942             return t.compiled.call(this, vs, parent);
60943         } catch (e) {
60944             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60945             Roo.log(e.toString());
60946             Roo.log(t.compiled);
60947             return '';
60948         }
60949     },
60950
60951     compileTpl : function(tpl)
60952     {
60953         var fm = Roo.util.Format;
60954         var useF = this.disableFormats !== true;
60955         var sep = Roo.isGecko ? "+" : ",";
60956         var undef = function(str) {
60957             Roo.log("Property not found :"  + str);
60958             return '';
60959         };
60960         
60961         var fn = function(m, name, format, args)
60962         {
60963             //Roo.log(arguments);
60964             args = args ? args.replace(/\\'/g,"'") : args;
60965             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60966             if (typeof(format) == 'undefined') {
60967                 format= 'htmlEncode';
60968             }
60969             if (format == 'raw' ) {
60970                 format = false;
60971             }
60972             
60973             if(name.substr(0, 4) == 'xtpl'){
60974                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60975             }
60976             
60977             // build an array of options to determine if value is undefined..
60978             
60979             // basically get 'xxxx.yyyy' then do
60980             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60981             //    (function () { Roo.log("Property not found"); return ''; })() :
60982             //    ......
60983             
60984             var udef_ar = [];
60985             var lookfor = '';
60986             Roo.each(name.split('.'), function(st) {
60987                 lookfor += (lookfor.length ? '.': '') + st;
60988                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60989             });
60990             
60991             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60992             
60993             
60994             if(format && useF){
60995                 
60996                 args = args ? ',' + args : "";
60997                  
60998                 if(format.substr(0, 5) != "this."){
60999                     format = "fm." + format + '(';
61000                 }else{
61001                     format = 'this.call("'+ format.substr(5) + '", ';
61002                     args = ", values";
61003                 }
61004                 
61005                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61006             }
61007              
61008             if (args.length) {
61009                 // called with xxyx.yuu:(test,test)
61010                 // change to ()
61011                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61012             }
61013             // raw.. - :raw modifier..
61014             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61015             
61016         };
61017         var body;
61018         // branched to use + in gecko and [].join() in others
61019         if(Roo.isGecko){
61020             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61021                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61022                     "';};};";
61023         }else{
61024             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61025             body.push(tpl.body.replace(/(\r\n|\n)/g,
61026                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61027             body.push("'].join('');};};");
61028             body = body.join('');
61029         }
61030         
61031         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61032        
61033         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61034         eval(body);
61035         
61036         return this;
61037     },
61038
61039     applyTemplate : function(values){
61040         return this.master.compiled.call(this, values, {});
61041         //var s = this.subs;
61042     },
61043
61044     apply : function(){
61045         return this.applyTemplate.apply(this, arguments);
61046     }
61047
61048  });
61049
61050 Roo.XTemplate.from = function(el){
61051     el = Roo.getDom(el);
61052     return new Roo.XTemplate(el.value || el.innerHTML);
61053 };