roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     }
1019     
1020 });
1021
1022
1023  
1024 /*
1025  * Based on:
1026  * Ext JS Library 1.1.1
1027  * Copyright(c) 2006-2007, Ext JS, LLC.
1028  *
1029  * Originally Released Under LGPL - original licence link has changed is not relivant.
1030  *
1031  * Fork - LGPL
1032  * <script type="text/javascript">
1033  */
1034
1035 /**
1036  * @class Date
1037  *
1038  * The date parsing and format syntax is a subset of
1039  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1040  * supported will provide results equivalent to their PHP versions.
1041  *
1042  * Following is the list of all currently supported formats:
1043  *<pre>
1044 Sample date:
1045 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1046
1047 Format  Output      Description
1048 ------  ----------  --------------------------------------------------------------
1049   d      10         Day of the month, 2 digits with leading zeros
1050   D      Wed        A textual representation of a day, three letters
1051   j      10         Day of the month without leading zeros
1052   l      Wednesday  A full textual representation of the day of the week
1053   S      th         English ordinal day of month suffix, 2 chars (use with j)
1054   w      3          Numeric representation of the day of the week
1055   z      9          The julian date, or day of the year (0-365)
1056   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1057   F      January    A full textual representation of the month
1058   m      01         Numeric representation of a month, with leading zeros
1059   M      Jan        Month name abbreviation, three letters
1060   n      1          Numeric representation of a month, without leading zeros
1061   t      31         Number of days in the given month
1062   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1063   Y      2007       A full numeric representation of a year, 4 digits
1064   y      07         A two digit representation of a year
1065   a      pm         Lowercase Ante meridiem and Post meridiem
1066   A      PM         Uppercase Ante meridiem and Post meridiem
1067   g      3          12-hour format of an hour without leading zeros
1068   G      15         24-hour format of an hour without leading zeros
1069   h      03         12-hour format of an hour with leading zeros
1070   H      15         24-hour format of an hour with leading zeros
1071   i      05         Minutes with leading zeros
1072   s      01         Seconds, with leading zeros
1073   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1074   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1075   T      CST        Timezone setting of the machine running the code
1076   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1077 </pre>
1078  *
1079  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1080  * <pre><code>
1081 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1082 document.write(dt.format('Y-m-d'));                         //2007-01-10
1083 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1084 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1085  </code></pre>
1086  *
1087  * Here are some standard date/time patterns that you might find helpful.  They
1088  * are not part of the source of Date.js, but to use them you can simply copy this
1089  * block of code into any script that is included after Date.js and they will also become
1090  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1091  * <pre><code>
1092 Date.patterns = {
1093     ISO8601Long:"Y-m-d H:i:s",
1094     ISO8601Short:"Y-m-d",
1095     ShortDate: "n/j/Y",
1096     LongDate: "l, F d, Y",
1097     FullDateTime: "l, F d, Y g:i:s A",
1098     MonthDay: "F d",
1099     ShortTime: "g:i A",
1100     LongTime: "g:i:s A",
1101     SortableDateTime: "Y-m-d\\TH:i:s",
1102     UniversalSortableDateTime: "Y-m-d H:i:sO",
1103     YearMonth: "F, Y"
1104 };
1105 </code></pre>
1106  *
1107  * Example usage:
1108  * <pre><code>
1109 var dt = new Date();
1110 document.write(dt.format(Date.patterns.ShortDate));
1111  </code></pre>
1112  */
1113
1114 /*
1115  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1116  * They generate precompiled functions from date formats instead of parsing and
1117  * processing the pattern every time you format a date.  These functions are available
1118  * on every Date object (any javascript function).
1119  *
1120  * The original article and download are here:
1121  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1122  *
1123  */
1124  
1125  
1126  // was in core
1127 /**
1128  Returns the number of milliseconds between this date and date
1129  @param {Date} date (optional) Defaults to now
1130  @return {Number} The diff in milliseconds
1131  @member Date getElapsed
1132  */
1133 Date.prototype.getElapsed = function(date) {
1134         return Math.abs((date || new Date()).getTime()-this.getTime());
1135 };
1136 // was in date file..
1137
1138
1139 // private
1140 Date.parseFunctions = {count:0};
1141 // private
1142 Date.parseRegexes = [];
1143 // private
1144 Date.formatFunctions = {count:0};
1145
1146 // private
1147 Date.prototype.dateFormat = function(format) {
1148     if (Date.formatFunctions[format] == null) {
1149         Date.createNewFormat(format);
1150     }
1151     var func = Date.formatFunctions[format];
1152     return this[func]();
1153 };
1154
1155
1156 /**
1157  * Formats a date given the supplied format string
1158  * @param {String} format The format string
1159  * @return {String} The formatted date
1160  * @method
1161  */
1162 Date.prototype.format = Date.prototype.dateFormat;
1163
1164 // private
1165 Date.createNewFormat = function(format) {
1166     var funcName = "format" + Date.formatFunctions.count++;
1167     Date.formatFunctions[format] = funcName;
1168     var code = "Date.prototype." + funcName + " = function(){return ";
1169     var special = false;
1170     var ch = '';
1171     for (var i = 0; i < format.length; ++i) {
1172         ch = format.charAt(i);
1173         if (!special && ch == "\\") {
1174             special = true;
1175         }
1176         else if (special) {
1177             special = false;
1178             code += "'" + String.escape(ch) + "' + ";
1179         }
1180         else {
1181             code += Date.getFormatCode(ch);
1182         }
1183     }
1184     /** eval:var:zzzzzzzzzzzzz */
1185     eval(code.substring(0, code.length - 3) + ";}");
1186 };
1187
1188 // private
1189 Date.getFormatCode = function(character) {
1190     switch (character) {
1191     case "d":
1192         return "String.leftPad(this.getDate(), 2, '0') + ";
1193     case "D":
1194         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1195     case "j":
1196         return "this.getDate() + ";
1197     case "l":
1198         return "Date.dayNames[this.getDay()] + ";
1199     case "S":
1200         return "this.getSuffix() + ";
1201     case "w":
1202         return "this.getDay() + ";
1203     case "z":
1204         return "this.getDayOfYear() + ";
1205     case "W":
1206         return "this.getWeekOfYear() + ";
1207     case "F":
1208         return "Date.monthNames[this.getMonth()] + ";
1209     case "m":
1210         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1211     case "M":
1212         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1213     case "n":
1214         return "(this.getMonth() + 1) + ";
1215     case "t":
1216         return "this.getDaysInMonth() + ";
1217     case "L":
1218         return "(this.isLeapYear() ? 1 : 0) + ";
1219     case "Y":
1220         return "this.getFullYear() + ";
1221     case "y":
1222         return "('' + this.getFullYear()).substring(2, 4) + ";
1223     case "a":
1224         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1225     case "A":
1226         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1227     case "g":
1228         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1229     case "G":
1230         return "this.getHours() + ";
1231     case "h":
1232         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1233     case "H":
1234         return "String.leftPad(this.getHours(), 2, '0') + ";
1235     case "i":
1236         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1237     case "s":
1238         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1239     case "O":
1240         return "this.getGMTOffset() + ";
1241     case "P":
1242         return "this.getGMTColonOffset() + ";
1243     case "T":
1244         return "this.getTimezone() + ";
1245     case "Z":
1246         return "(this.getTimezoneOffset() * -60) + ";
1247     default:
1248         return "'" + String.escape(character) + "' + ";
1249     }
1250 };
1251
1252 /**
1253  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1254  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1255  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1256  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1257  * string or the parse operation will fail.
1258  * Example Usage:
1259 <pre><code>
1260 //dt = Fri May 25 2007 (current date)
1261 var dt = new Date();
1262
1263 //dt = Thu May 25 2006 (today's month/day in 2006)
1264 dt = Date.parseDate("2006", "Y");
1265
1266 //dt = Sun Jan 15 2006 (all date parts specified)
1267 dt = Date.parseDate("2006-1-15", "Y-m-d");
1268
1269 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1270 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1271 </code></pre>
1272  * @param {String} input The unparsed date as a string
1273  * @param {String} format The format the date is in
1274  * @return {Date} The parsed date
1275  * @static
1276  */
1277 Date.parseDate = function(input, format) {
1278     if (Date.parseFunctions[format] == null) {
1279         Date.createParser(format);
1280     }
1281     var func = Date.parseFunctions[format];
1282     return Date[func](input);
1283 };
1284 /**
1285  * @private
1286  */
1287
1288 Date.createParser = function(format) {
1289     var funcName = "parse" + Date.parseFunctions.count++;
1290     var regexNum = Date.parseRegexes.length;
1291     var currentGroup = 1;
1292     Date.parseFunctions[format] = funcName;
1293
1294     var code = "Date." + funcName + " = function(input){\n"
1295         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1296         + "var d = new Date();\n"
1297         + "y = d.getFullYear();\n"
1298         + "m = d.getMonth();\n"
1299         + "d = d.getDate();\n"
1300         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1301         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1302         + "if (results && results.length > 0) {";
1303     var regex = "";
1304
1305     var special = false;
1306     var ch = '';
1307     for (var i = 0; i < format.length; ++i) {
1308         ch = format.charAt(i);
1309         if (!special && ch == "\\") {
1310             special = true;
1311         }
1312         else if (special) {
1313             special = false;
1314             regex += String.escape(ch);
1315         }
1316         else {
1317             var obj = Date.formatCodeToRegex(ch, currentGroup);
1318             currentGroup += obj.g;
1319             regex += obj.s;
1320             if (obj.g && obj.c) {
1321                 code += obj.c;
1322             }
1323         }
1324     }
1325
1326     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1327         + "{v = new Date(y, m, d, h, i, s);}\n"
1328         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1329         + "{v = new Date(y, m, d, h, i);}\n"
1330         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1331         + "{v = new Date(y, m, d, h);}\n"
1332         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1333         + "{v = new Date(y, m, d);}\n"
1334         + "else if (y >= 0 && m >= 0)\n"
1335         + "{v = new Date(y, m);}\n"
1336         + "else if (y >= 0)\n"
1337         + "{v = new Date(y);}\n"
1338         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1339         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1340         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1341         + ";}";
1342
1343     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1344     /** eval:var:zzzzzzzzzzzzz */
1345     eval(code);
1346 };
1347
1348 // private
1349 Date.formatCodeToRegex = function(character, currentGroup) {
1350     switch (character) {
1351     case "D":
1352         return {g:0,
1353         c:null,
1354         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1355     case "j":
1356         return {g:1,
1357             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1358             s:"(\\d{1,2})"}; // day of month without leading zeroes
1359     case "d":
1360         return {g:1,
1361             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1362             s:"(\\d{2})"}; // day of month with leading zeroes
1363     case "l":
1364         return {g:0,
1365             c:null,
1366             s:"(?:" + Date.dayNames.join("|") + ")"};
1367     case "S":
1368         return {g:0,
1369             c:null,
1370             s:"(?:st|nd|rd|th)"};
1371     case "w":
1372         return {g:0,
1373             c:null,
1374             s:"\\d"};
1375     case "z":
1376         return {g:0,
1377             c:null,
1378             s:"(?:\\d{1,3})"};
1379     case "W":
1380         return {g:0,
1381             c:null,
1382             s:"(?:\\d{2})"};
1383     case "F":
1384         return {g:1,
1385             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1386             s:"(" + Date.monthNames.join("|") + ")"};
1387     case "M":
1388         return {g:1,
1389             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1390             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1391     case "n":
1392         return {g:1,
1393             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1394             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1395     case "m":
1396         return {g:1,
1397             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1398             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1399     case "t":
1400         return {g:0,
1401             c:null,
1402             s:"\\d{1,2}"};
1403     case "L":
1404         return {g:0,
1405             c:null,
1406             s:"(?:1|0)"};
1407     case "Y":
1408         return {g:1,
1409             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1410             s:"(\\d{4})"};
1411     case "y":
1412         return {g:1,
1413             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1414                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1415             s:"(\\d{1,2})"};
1416     case "a":
1417         return {g:1,
1418             c:"if (results[" + currentGroup + "] == 'am') {\n"
1419                 + "if (h == 12) { h = 0; }\n"
1420                 + "} else { if (h < 12) { h += 12; }}",
1421             s:"(am|pm)"};
1422     case "A":
1423         return {g:1,
1424             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1425                 + "if (h == 12) { h = 0; }\n"
1426                 + "} else { if (h < 12) { h += 12; }}",
1427             s:"(AM|PM)"};
1428     case "g":
1429     case "G":
1430         return {g:1,
1431             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1432             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1433     case "h":
1434     case "H":
1435         return {g:1,
1436             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1437             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1438     case "i":
1439         return {g:1,
1440             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1441             s:"(\\d{2})"};
1442     case "s":
1443         return {g:1,
1444             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1445             s:"(\\d{2})"};
1446     case "O":
1447         return {g:1,
1448             c:[
1449                 "o = results[", currentGroup, "];\n",
1450                 "var sn = o.substring(0,1);\n", // get + / - sign
1451                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1452                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1453                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1454                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1455             ].join(""),
1456             s:"([+\-]\\d{2,4})"};
1457     
1458     
1459     case "P":
1460         return {g:1,
1461                 c:[
1462                    "o = results[", currentGroup, "];\n",
1463                    "var sn = o.substring(0,1);\n",
1464                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1465                    "var mn = o.substring(4,6) % 60;\n",
1466                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1467                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1468             ].join(""),
1469             s:"([+\-]\\d{4})"};
1470     case "T":
1471         return {g:0,
1472             c:null,
1473             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1474     case "Z":
1475         return {g:1,
1476             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1477                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1478             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1479     default:
1480         return {g:0,
1481             c:null,
1482             s:String.escape(character)};
1483     }
1484 };
1485
1486 /**
1487  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1488  * @return {String} The abbreviated timezone name (e.g. 'CST')
1489  */
1490 Date.prototype.getTimezone = function() {
1491     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1492 };
1493
1494 /**
1495  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1496  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1497  */
1498 Date.prototype.getGMTOffset = function() {
1499     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1500         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1501         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1502 };
1503
1504 /**
1505  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1506  * @return {String} 2-characters representing hours and 2-characters representing minutes
1507  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1508  */
1509 Date.prototype.getGMTColonOffset = function() {
1510         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1511                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1512                 + ":"
1513                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1514 }
1515
1516 /**
1517  * Get the numeric day number of the year, adjusted for leap year.
1518  * @return {Number} 0 through 364 (365 in leap years)
1519  */
1520 Date.prototype.getDayOfYear = function() {
1521     var num = 0;
1522     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1523     for (var i = 0; i < this.getMonth(); ++i) {
1524         num += Date.daysInMonth[i];
1525     }
1526     return num + this.getDate() - 1;
1527 };
1528
1529 /**
1530  * Get the string representation of the numeric week number of the year
1531  * (equivalent to the format specifier 'W').
1532  * @return {String} '00' through '52'
1533  */
1534 Date.prototype.getWeekOfYear = function() {
1535     // Skip to Thursday of this week
1536     var now = this.getDayOfYear() + (4 - this.getDay());
1537     // Find the first Thursday of the year
1538     var jan1 = new Date(this.getFullYear(), 0, 1);
1539     var then = (7 - jan1.getDay() + 4);
1540     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1541 };
1542
1543 /**
1544  * Whether or not the current date is in a leap year.
1545  * @return {Boolean} True if the current date is in a leap year, else false
1546  */
1547 Date.prototype.isLeapYear = function() {
1548     var year = this.getFullYear();
1549     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1550 };
1551
1552 /**
1553  * Get the first day of the current month, adjusted for leap year.  The returned value
1554  * is the numeric day index within the week (0-6) which can be used in conjunction with
1555  * the {@link #monthNames} array to retrieve the textual day name.
1556  * Example:
1557  *<pre><code>
1558 var dt = new Date('1/10/2007');
1559 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1560 </code></pre>
1561  * @return {Number} The day number (0-6)
1562  */
1563 Date.prototype.getFirstDayOfMonth = function() {
1564     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1565     return (day < 0) ? (day + 7) : day;
1566 };
1567
1568 /**
1569  * Get the last day of the current month, adjusted for leap year.  The returned value
1570  * is the numeric day index within the week (0-6) which can be used in conjunction with
1571  * the {@link #monthNames} array to retrieve the textual day name.
1572  * Example:
1573  *<pre><code>
1574 var dt = new Date('1/10/2007');
1575 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1576 </code></pre>
1577  * @return {Number} The day number (0-6)
1578  */
1579 Date.prototype.getLastDayOfMonth = function() {
1580     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1581     return (day < 0) ? (day + 7) : day;
1582 };
1583
1584
1585 /**
1586  * Get the first date of this date's month
1587  * @return {Date}
1588  */
1589 Date.prototype.getFirstDateOfMonth = function() {
1590     return new Date(this.getFullYear(), this.getMonth(), 1);
1591 };
1592
1593 /**
1594  * Get the last date of this date's month
1595  * @return {Date}
1596  */
1597 Date.prototype.getLastDateOfMonth = function() {
1598     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1599 };
1600 /**
1601  * Get the number of days in the current month, adjusted for leap year.
1602  * @return {Number} The number of days in the month
1603  */
1604 Date.prototype.getDaysInMonth = function() {
1605     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1606     return Date.daysInMonth[this.getMonth()];
1607 };
1608
1609 /**
1610  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1611  * @return {String} 'st, 'nd', 'rd' or 'th'
1612  */
1613 Date.prototype.getSuffix = function() {
1614     switch (this.getDate()) {
1615         case 1:
1616         case 21:
1617         case 31:
1618             return "st";
1619         case 2:
1620         case 22:
1621             return "nd";
1622         case 3:
1623         case 23:
1624             return "rd";
1625         default:
1626             return "th";
1627     }
1628 };
1629
1630 // private
1631 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1632
1633 /**
1634  * An array of textual month names.
1635  * Override these values for international dates, for example...
1636  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1637  * @type Array
1638  * @static
1639  */
1640 Date.monthNames =
1641    ["January",
1642     "February",
1643     "March",
1644     "April",
1645     "May",
1646     "June",
1647     "July",
1648     "August",
1649     "September",
1650     "October",
1651     "November",
1652     "December"];
1653
1654 /**
1655  * An array of textual day names.
1656  * Override these values for international dates, for example...
1657  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1658  * @type Array
1659  * @static
1660  */
1661 Date.dayNames =
1662    ["Sunday",
1663     "Monday",
1664     "Tuesday",
1665     "Wednesday",
1666     "Thursday",
1667     "Friday",
1668     "Saturday"];
1669
1670 // private
1671 Date.y2kYear = 50;
1672 // private
1673 Date.monthNumbers = {
1674     Jan:0,
1675     Feb:1,
1676     Mar:2,
1677     Apr:3,
1678     May:4,
1679     Jun:5,
1680     Jul:6,
1681     Aug:7,
1682     Sep:8,
1683     Oct:9,
1684     Nov:10,
1685     Dec:11};
1686
1687 /**
1688  * Creates and returns a new Date instance with the exact same date value as the called instance.
1689  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1690  * variable will also be changed.  When the intention is to create a new variable that will not
1691  * modify the original instance, you should create a clone.
1692  *
1693  * Example of correctly cloning a date:
1694  * <pre><code>
1695 //wrong way:
1696 var orig = new Date('10/1/2006');
1697 var copy = orig;
1698 copy.setDate(5);
1699 document.write(orig);  //returns 'Thu Oct 05 2006'!
1700
1701 //correct way:
1702 var orig = new Date('10/1/2006');
1703 var copy = orig.clone();
1704 copy.setDate(5);
1705 document.write(orig);  //returns 'Thu Oct 01 2006'
1706 </code></pre>
1707  * @return {Date} The new Date instance
1708  */
1709 Date.prototype.clone = function() {
1710         return new Date(this.getTime());
1711 };
1712
1713 /**
1714  * Clears any time information from this date
1715  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1716  @return {Date} this or the clone
1717  */
1718 Date.prototype.clearTime = function(clone){
1719     if(clone){
1720         return this.clone().clearTime();
1721     }
1722     this.setHours(0);
1723     this.setMinutes(0);
1724     this.setSeconds(0);
1725     this.setMilliseconds(0);
1726     return this;
1727 };
1728
1729 // private
1730 // safari setMonth is broken -- check that this is only donw once...
1731 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1732     Date.brokenSetMonth = Date.prototype.setMonth;
1733         Date.prototype.setMonth = function(num){
1734                 if(num <= -1){
1735                         var n = Math.ceil(-num);
1736                         var back_year = Math.ceil(n/12);
1737                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1738                         this.setFullYear(this.getFullYear() - back_year);
1739                         return Date.brokenSetMonth.call(this, month);
1740                 } else {
1741                         return Date.brokenSetMonth.apply(this, arguments);
1742                 }
1743         };
1744 }
1745
1746 /** Date interval constant 
1747 * @static 
1748 * @type String */
1749 Date.MILLI = "ms";
1750 /** Date interval constant 
1751 * @static 
1752 * @type String */
1753 Date.SECOND = "s";
1754 /** Date interval constant 
1755 * @static 
1756 * @type String */
1757 Date.MINUTE = "mi";
1758 /** Date interval constant 
1759 * @static 
1760 * @type String */
1761 Date.HOUR = "h";
1762 /** Date interval constant 
1763 * @static 
1764 * @type String */
1765 Date.DAY = "d";
1766 /** Date interval constant 
1767 * @static 
1768 * @type String */
1769 Date.MONTH = "mo";
1770 /** Date interval constant 
1771 * @static 
1772 * @type String */
1773 Date.YEAR = "y";
1774
1775 /**
1776  * Provides a convenient method of performing basic date arithmetic.  This method
1777  * does not modify the Date instance being called - it creates and returns
1778  * a new Date instance containing the resulting date value.
1779  *
1780  * Examples:
1781  * <pre><code>
1782 //Basic usage:
1783 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1784 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1785
1786 //Negative values will subtract correctly:
1787 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1788 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1789
1790 //You can even chain several calls together in one line!
1791 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1792 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1793  </code></pre>
1794  *
1795  * @param {String} interval   A valid date interval enum value
1796  * @param {Number} value      The amount to add to the current date
1797  * @return {Date} The new Date instance
1798  */
1799 Date.prototype.add = function(interval, value){
1800   var d = this.clone();
1801   if (!interval || value === 0) { return d; }
1802   switch(interval.toLowerCase()){
1803     case Date.MILLI:
1804       d.setMilliseconds(this.getMilliseconds() + value);
1805       break;
1806     case Date.SECOND:
1807       d.setSeconds(this.getSeconds() + value);
1808       break;
1809     case Date.MINUTE:
1810       d.setMinutes(this.getMinutes() + value);
1811       break;
1812     case Date.HOUR:
1813       d.setHours(this.getHours() + value);
1814       break;
1815     case Date.DAY:
1816       d.setDate(this.getDate() + value);
1817       break;
1818     case Date.MONTH:
1819       var day = this.getDate();
1820       if(day > 28){
1821           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1822       }
1823       d.setDate(day);
1824       d.setMonth(this.getMonth() + value);
1825       break;
1826     case Date.YEAR:
1827       d.setFullYear(this.getFullYear() + value);
1828       break;
1829   }
1830   return d;
1831 };
1832 /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842
1843 /**
1844  * @class Roo.lib.Dom
1845  * @static
1846  * 
1847  * Dom utils (from YIU afaik)
1848  * 
1849  **/
1850 Roo.lib.Dom = {
1851     /**
1852      * Get the view width
1853      * @param {Boolean} full True will get the full document, otherwise it's the view width
1854      * @return {Number} The width
1855      */
1856      
1857     getViewWidth : function(full) {
1858         return full ? this.getDocumentWidth() : this.getViewportWidth();
1859     },
1860     /**
1861      * Get the view height
1862      * @param {Boolean} full True will get the full document, otherwise it's the view height
1863      * @return {Number} The height
1864      */
1865     getViewHeight : function(full) {
1866         return full ? this.getDocumentHeight() : this.getViewportHeight();
1867     },
1868
1869     getDocumentHeight: function() {
1870         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1871         return Math.max(scrollHeight, this.getViewportHeight());
1872     },
1873
1874     getDocumentWidth: function() {
1875         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1876         return Math.max(scrollWidth, this.getViewportWidth());
1877     },
1878
1879     getViewportHeight: function() {
1880         var height = self.innerHeight;
1881         var mode = document.compatMode;
1882
1883         if ((mode || Roo.isIE) && !Roo.isOpera) {
1884             height = (mode == "CSS1Compat") ?
1885                      document.documentElement.clientHeight :
1886                      document.body.clientHeight;
1887         }
1888
1889         return height;
1890     },
1891
1892     getViewportWidth: function() {
1893         var width = self.innerWidth;
1894         var mode = document.compatMode;
1895
1896         if (mode || Roo.isIE) {
1897             width = (mode == "CSS1Compat") ?
1898                     document.documentElement.clientWidth :
1899                     document.body.clientWidth;
1900         }
1901         return width;
1902     },
1903
1904     isAncestor : function(p, c) {
1905         p = Roo.getDom(p);
1906         c = Roo.getDom(c);
1907         if (!p || !c) {
1908             return false;
1909         }
1910
1911         if (p.contains && !Roo.isSafari) {
1912             return p.contains(c);
1913         } else if (p.compareDocumentPosition) {
1914             return !!(p.compareDocumentPosition(c) & 16);
1915         } else {
1916             var parent = c.parentNode;
1917             while (parent) {
1918                 if (parent == p) {
1919                     return true;
1920                 }
1921                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1922                     return false;
1923                 }
1924                 parent = parent.parentNode;
1925             }
1926             return false;
1927         }
1928     },
1929
1930     getRegion : function(el) {
1931         return Roo.lib.Region.getRegion(el);
1932     },
1933
1934     getY : function(el) {
1935         return this.getXY(el)[1];
1936     },
1937
1938     getX : function(el) {
1939         return this.getXY(el)[0];
1940     },
1941
1942     getXY : function(el) {
1943         var p, pe, b, scroll, bd = document.body;
1944         el = Roo.getDom(el);
1945         var fly = Roo.lib.AnimBase.fly;
1946         if (el.getBoundingClientRect) {
1947             b = el.getBoundingClientRect();
1948             scroll = fly(document).getScroll();
1949             return [b.left + scroll.left, b.top + scroll.top];
1950         }
1951         var x = 0, y = 0;
1952
1953         p = el;
1954
1955         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1956
1957         while (p) {
1958
1959             x += p.offsetLeft;
1960             y += p.offsetTop;
1961
1962             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1963                 hasAbsolute = true;
1964             }
1965
1966             if (Roo.isGecko) {
1967                 pe = fly(p);
1968
1969                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1970                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1971
1972
1973                 x += bl;
1974                 y += bt;
1975
1976
1977                 if (p != el && pe.getStyle('overflow') != 'visible') {
1978                     x += bl;
1979                     y += bt;
1980                 }
1981             }
1982             p = p.offsetParent;
1983         }
1984
1985         if (Roo.isSafari && hasAbsolute) {
1986             x -= bd.offsetLeft;
1987             y -= bd.offsetTop;
1988         }
1989
1990         if (Roo.isGecko && !hasAbsolute) {
1991             var dbd = fly(bd);
1992             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1993             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1994         }
1995
1996         p = el.parentNode;
1997         while (p && p != bd) {
1998             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1999                 x -= p.scrollLeft;
2000                 y -= p.scrollTop;
2001             }
2002             p = p.parentNode;
2003         }
2004         return [x, y];
2005     },
2006  
2007   
2008
2009
2010     setXY : function(el, xy) {
2011         el = Roo.fly(el, '_setXY');
2012         el.position();
2013         var pts = el.translatePoints(xy);
2014         if (xy[0] !== false) {
2015             el.dom.style.left = pts.left + "px";
2016         }
2017         if (xy[1] !== false) {
2018             el.dom.style.top = pts.top + "px";
2019         }
2020     },
2021
2022     setX : function(el, x) {
2023         this.setXY(el, [x, false]);
2024     },
2025
2026     setY : function(el, y) {
2027         this.setXY(el, [false, y]);
2028     }
2029 };
2030 /*
2031  * Portions of this file are based on pieces of Yahoo User Interface Library
2032  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2033  * YUI licensed under the BSD License:
2034  * http://developer.yahoo.net/yui/license.txt
2035  * <script type="text/javascript">
2036  *
2037  */
2038
2039 Roo.lib.Event = function() {
2040     var loadComplete = false;
2041     var listeners = [];
2042     var unloadListeners = [];
2043     var retryCount = 0;
2044     var onAvailStack = [];
2045     var counter = 0;
2046     var lastError = null;
2047
2048     return {
2049         POLL_RETRYS: 200,
2050         POLL_INTERVAL: 20,
2051         EL: 0,
2052         TYPE: 1,
2053         FN: 2,
2054         WFN: 3,
2055         OBJ: 3,
2056         ADJ_SCOPE: 4,
2057         _interval: null,
2058
2059         startInterval: function() {
2060             if (!this._interval) {
2061                 var self = this;
2062                 var callback = function() {
2063                     self._tryPreloadAttach();
2064                 };
2065                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2066
2067             }
2068         },
2069
2070         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2071             onAvailStack.push({ id:         p_id,
2072                 fn:         p_fn,
2073                 obj:        p_obj,
2074                 override:   p_override,
2075                 checkReady: false    });
2076
2077             retryCount = this.POLL_RETRYS;
2078             this.startInterval();
2079         },
2080
2081
2082         addListener: function(el, eventName, fn) {
2083             el = Roo.getDom(el);
2084             if (!el || !fn) {
2085                 return false;
2086             }
2087
2088             if ("unload" == eventName) {
2089                 unloadListeners[unloadListeners.length] =
2090                 [el, eventName, fn];
2091                 return true;
2092             }
2093
2094             var wrappedFn = function(e) {
2095                 return fn(Roo.lib.Event.getEvent(e));
2096             };
2097
2098             var li = [el, eventName, fn, wrappedFn];
2099
2100             var index = listeners.length;
2101             listeners[index] = li;
2102
2103             this.doAdd(el, eventName, wrappedFn, false);
2104             return true;
2105
2106         },
2107
2108
2109         removeListener: function(el, eventName, fn) {
2110             var i, len;
2111
2112             el = Roo.getDom(el);
2113
2114             if(!fn) {
2115                 return this.purgeElement(el, false, eventName);
2116             }
2117
2118
2119             if ("unload" == eventName) {
2120
2121                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2122                     var li = unloadListeners[i];
2123                     if (li &&
2124                         li[0] == el &&
2125                         li[1] == eventName &&
2126                         li[2] == fn) {
2127                         unloadListeners.splice(i, 1);
2128                         return true;
2129                     }
2130                 }
2131
2132                 return false;
2133             }
2134
2135             var cacheItem = null;
2136
2137
2138             var index = arguments[3];
2139
2140             if ("undefined" == typeof index) {
2141                 index = this._getCacheIndex(el, eventName, fn);
2142             }
2143
2144             if (index >= 0) {
2145                 cacheItem = listeners[index];
2146             }
2147
2148             if (!el || !cacheItem) {
2149                 return false;
2150             }
2151
2152             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2153
2154             delete listeners[index][this.WFN];
2155             delete listeners[index][this.FN];
2156             listeners.splice(index, 1);
2157
2158             return true;
2159
2160         },
2161
2162
2163         getTarget: function(ev, resolveTextNode) {
2164             ev = ev.browserEvent || ev;
2165             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2166             var t = ev.target || ev.srcElement;
2167             return this.resolveTextNode(t);
2168         },
2169
2170
2171         resolveTextNode: function(node) {
2172             if (Roo.isSafari && node && 3 == node.nodeType) {
2173                 return node.parentNode;
2174             } else {
2175                 return node;
2176             }
2177         },
2178
2179
2180         getPageX: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2183             var x = ev.pageX;
2184             if (!x && 0 !== x) {
2185                 x = ev.clientX || 0;
2186
2187                 if (Roo.isIE) {
2188                     x += this.getScroll()[1];
2189                 }
2190             }
2191
2192             return x;
2193         },
2194
2195
2196         getPageY: function(ev) {
2197             ev = ev.browserEvent || ev;
2198             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2199             var y = ev.pageY;
2200             if (!y && 0 !== y) {
2201                 y = ev.clientY || 0;
2202
2203                 if (Roo.isIE) {
2204                     y += this.getScroll()[0];
2205                 }
2206             }
2207
2208
2209             return y;
2210         },
2211
2212
2213         getXY: function(ev) {
2214             ev = ev.browserEvent || ev;
2215             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2216             return [this.getPageX(ev), this.getPageY(ev)];
2217         },
2218
2219
2220         getRelatedTarget: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2223             var t = ev.relatedTarget;
2224             if (!t) {
2225                 if (ev.type == "mouseout") {
2226                     t = ev.toElement;
2227                 } else if (ev.type == "mouseover") {
2228                     t = ev.fromElement;
2229                 }
2230             }
2231
2232             return this.resolveTextNode(t);
2233         },
2234
2235
2236         getTime: function(ev) {
2237             ev = ev.browserEvent || ev;
2238             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2239             if (!ev.time) {
2240                 var t = new Date().getTime();
2241                 try {
2242                     ev.time = t;
2243                 } catch(ex) {
2244                     this.lastError = ex;
2245                     return t;
2246                 }
2247             }
2248
2249             return ev.time;
2250         },
2251
2252
2253         stopEvent: function(ev) {
2254             this.stopPropagation(ev);
2255             this.preventDefault(ev);
2256         },
2257
2258
2259         stopPropagation: function(ev) {
2260             ev = ev.browserEvent || ev;
2261             if (ev.stopPropagation) {
2262                 ev.stopPropagation();
2263             } else {
2264                 ev.cancelBubble = true;
2265             }
2266         },
2267
2268
2269         preventDefault: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             if(ev.preventDefault) {
2272                 ev.preventDefault();
2273             } else {
2274                 ev.returnValue = false;
2275             }
2276         },
2277
2278
2279         getEvent: function(e) {
2280             var ev = e || window.event;
2281             if (!ev) {
2282                 var c = this.getEvent.caller;
2283                 while (c) {
2284                     ev = c.arguments[0];
2285                     if (ev && Event == ev.constructor) {
2286                         break;
2287                     }
2288                     c = c.caller;
2289                 }
2290             }
2291             return ev;
2292         },
2293
2294
2295         getCharCode: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             return ev.charCode || ev.keyCode || 0;
2298         },
2299
2300
2301         _getCacheIndex: function(el, eventName, fn) {
2302             for (var i = 0,len = listeners.length; i < len; ++i) {
2303                 var li = listeners[i];
2304                 if (li &&
2305                     li[this.FN] == fn &&
2306                     li[this.EL] == el &&
2307                     li[this.TYPE] == eventName) {
2308                     return i;
2309                 }
2310             }
2311
2312             return -1;
2313         },
2314
2315
2316         elCache: {},
2317
2318
2319         getEl: function(id) {
2320             return document.getElementById(id);
2321         },
2322
2323
2324         clearCache: function() {
2325         },
2326
2327
2328         _load: function(e) {
2329             loadComplete = true;
2330             var EU = Roo.lib.Event;
2331
2332
2333             if (Roo.isIE) {
2334                 EU.doRemove(window, "load", EU._load);
2335             }
2336         },
2337
2338
2339         _tryPreloadAttach: function() {
2340
2341             if (this.locked) {
2342                 return false;
2343             }
2344
2345             this.locked = true;
2346
2347
2348             var tryAgain = !loadComplete;
2349             if (!tryAgain) {
2350                 tryAgain = (retryCount > 0);
2351             }
2352
2353
2354             var notAvail = [];
2355             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2356                 var item = onAvailStack[i];
2357                 if (item) {
2358                     var el = this.getEl(item.id);
2359
2360                     if (el) {
2361                         if (!item.checkReady ||
2362                             loadComplete ||
2363                             el.nextSibling ||
2364                             (document && document.body)) {
2365
2366                             var scope = el;
2367                             if (item.override) {
2368                                 if (item.override === true) {
2369                                     scope = item.obj;
2370                                 } else {
2371                                     scope = item.override;
2372                                 }
2373                             }
2374                             item.fn.call(scope, item.obj);
2375                             onAvailStack[i] = null;
2376                         }
2377                     } else {
2378                         notAvail.push(item);
2379                     }
2380                 }
2381             }
2382
2383             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2384
2385             if (tryAgain) {
2386
2387                 this.startInterval();
2388             } else {
2389                 clearInterval(this._interval);
2390                 this._interval = null;
2391             }
2392
2393             this.locked = false;
2394
2395             return true;
2396
2397         },
2398
2399
2400         purgeElement: function(el, recurse, eventName) {
2401             var elListeners = this.getListeners(el, eventName);
2402             if (elListeners) {
2403                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2404                     var l = elListeners[i];
2405                     this.removeListener(el, l.type, l.fn);
2406                 }
2407             }
2408
2409             if (recurse && el && el.childNodes) {
2410                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2411                     this.purgeElement(el.childNodes[i], recurse, eventName);
2412                 }
2413             }
2414         },
2415
2416
2417         getListeners: function(el, eventName) {
2418             var results = [], searchLists;
2419             if (!eventName) {
2420                 searchLists = [listeners, unloadListeners];
2421             } else if (eventName == "unload") {
2422                 searchLists = [unloadListeners];
2423             } else {
2424                 searchLists = [listeners];
2425             }
2426
2427             for (var j = 0; j < searchLists.length; ++j) {
2428                 var searchList = searchLists[j];
2429                 if (searchList && searchList.length > 0) {
2430                     for (var i = 0,len = searchList.length; i < len; ++i) {
2431                         var l = searchList[i];
2432                         if (l && l[this.EL] === el &&
2433                             (!eventName || eventName === l[this.TYPE])) {
2434                             results.push({
2435                                 type:   l[this.TYPE],
2436                                 fn:     l[this.FN],
2437                                 obj:    l[this.OBJ],
2438                                 adjust: l[this.ADJ_SCOPE],
2439                                 index:  i
2440                             });
2441                         }
2442                     }
2443                 }
2444             }
2445
2446             return (results.length) ? results : null;
2447         },
2448
2449
2450         _unload: function(e) {
2451
2452             var EU = Roo.lib.Event, i, j, l, len, index;
2453
2454             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2455                 l = unloadListeners[i];
2456                 if (l) {
2457                     var scope = window;
2458                     if (l[EU.ADJ_SCOPE]) {
2459                         if (l[EU.ADJ_SCOPE] === true) {
2460                             scope = l[EU.OBJ];
2461                         } else {
2462                             scope = l[EU.ADJ_SCOPE];
2463                         }
2464                     }
2465                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2466                     unloadListeners[i] = null;
2467                     l = null;
2468                     scope = null;
2469                 }
2470             }
2471
2472             unloadListeners = null;
2473
2474             if (listeners && listeners.length > 0) {
2475                 j = listeners.length;
2476                 while (j) {
2477                     index = j - 1;
2478                     l = listeners[index];
2479                     if (l) {
2480                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2481                                 l[EU.FN], index);
2482                     }
2483                     j = j - 1;
2484                 }
2485                 l = null;
2486
2487                 EU.clearCache();
2488             }
2489
2490             EU.doRemove(window, "unload", EU._unload);
2491
2492         },
2493
2494
2495         getScroll: function() {
2496             var dd = document.documentElement, db = document.body;
2497             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2498                 return [dd.scrollTop, dd.scrollLeft];
2499             } else if (db) {
2500                 return [db.scrollTop, db.scrollLeft];
2501             } else {
2502                 return [0, 0];
2503             }
2504         },
2505
2506
2507         doAdd: function () {
2508             if (window.addEventListener) {
2509                 return function(el, eventName, fn, capture) {
2510                     el.addEventListener(eventName, fn, (capture));
2511                 };
2512             } else if (window.attachEvent) {
2513                 return function(el, eventName, fn, capture) {
2514                     el.attachEvent("on" + eventName, fn);
2515                 };
2516             } else {
2517                 return function() {
2518                 };
2519             }
2520         }(),
2521
2522
2523         doRemove: function() {
2524             if (window.removeEventListener) {
2525                 return function (el, eventName, fn, capture) {
2526                     el.removeEventListener(eventName, fn, (capture));
2527                 };
2528             } else if (window.detachEvent) {
2529                 return function (el, eventName, fn) {
2530                     el.detachEvent("on" + eventName, fn);
2531                 };
2532             } else {
2533                 return function() {
2534                 };
2535             }
2536         }()
2537     };
2538     
2539 }();
2540 (function() {     
2541    
2542     var E = Roo.lib.Event;
2543     E.on = E.addListener;
2544     E.un = E.removeListener;
2545
2546     if (document && document.body) {
2547         E._load();
2548     } else {
2549         E.doAdd(window, "load", E._load);
2550     }
2551     E.doAdd(window, "unload", E._unload);
2552     E._tryPreloadAttach();
2553 })();
2554
2555 /*
2556  * Portions of this file are based on pieces of Yahoo User Interface Library
2557  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2558  * YUI licensed under the BSD License:
2559  * http://developer.yahoo.net/yui/license.txt
2560  * <script type="text/javascript">
2561  *
2562  */
2563
2564 (function() {
2565     /**
2566      * @class Roo.lib.Ajax
2567      *
2568      */
2569     Roo.lib.Ajax = {
2570         /**
2571          * @static 
2572          */
2573         request : function(method, uri, cb, data, options) {
2574             if(options){
2575                 var hs = options.headers;
2576                 if(hs){
2577                     for(var h in hs){
2578                         if(hs.hasOwnProperty(h)){
2579                             this.initHeader(h, hs[h], false);
2580                         }
2581                     }
2582                 }
2583                 if(options.xmlData){
2584                     this.initHeader('Content-Type', 'text/xml', false);
2585                     method = 'POST';
2586                     data = options.xmlData;
2587                 }
2588             }
2589
2590             return this.asyncRequest(method, uri, cb, data);
2591         },
2592
2593         serializeForm : function(form) {
2594             if(typeof form == 'string') {
2595                 form = (document.getElementById(form) || document.forms[form]);
2596             }
2597
2598             var el, name, val, disabled, data = '', hasSubmit = false;
2599             for (var i = 0; i < form.elements.length; i++) {
2600                 el = form.elements[i];
2601                 disabled = form.elements[i].disabled;
2602                 name = form.elements[i].name;
2603                 val = form.elements[i].value;
2604
2605                 if (!disabled && name){
2606                     switch (el.type)
2607                             {
2608                         case 'select-one':
2609                         case 'select-multiple':
2610                             for (var j = 0; j < el.options.length; j++) {
2611                                 if (el.options[j].selected) {
2612                                     if (Roo.isIE) {
2613                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2614                                     }
2615                                     else {
2616                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2617                                     }
2618                                 }
2619                             }
2620                             break;
2621                         case 'radio':
2622                         case 'checkbox':
2623                             if (el.checked) {
2624                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2625                             }
2626                             break;
2627                         case 'file':
2628
2629                         case undefined:
2630
2631                         case 'reset':
2632
2633                         case 'button':
2634
2635                             break;
2636                         case 'submit':
2637                             if(hasSubmit == false) {
2638                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2639                                 hasSubmit = true;
2640                             }
2641                             break;
2642                         default:
2643                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2644                             break;
2645                     }
2646                 }
2647             }
2648             data = data.substr(0, data.length - 1);
2649             return data;
2650         },
2651
2652         headers:{},
2653
2654         hasHeaders:false,
2655
2656         useDefaultHeader:true,
2657
2658         defaultPostHeader:'application/x-www-form-urlencoded',
2659
2660         useDefaultXhrHeader:true,
2661
2662         defaultXhrHeader:'XMLHttpRequest',
2663
2664         hasDefaultHeaders:true,
2665
2666         defaultHeaders:{},
2667
2668         poll:{},
2669
2670         timeout:{},
2671
2672         pollInterval:50,
2673
2674         transactionId:0,
2675
2676         setProgId:function(id)
2677         {
2678             this.activeX.unshift(id);
2679         },
2680
2681         setDefaultPostHeader:function(b)
2682         {
2683             this.useDefaultHeader = b;
2684         },
2685
2686         setDefaultXhrHeader:function(b)
2687         {
2688             this.useDefaultXhrHeader = b;
2689         },
2690
2691         setPollingInterval:function(i)
2692         {
2693             if (typeof i == 'number' && isFinite(i)) {
2694                 this.pollInterval = i;
2695             }
2696         },
2697
2698         createXhrObject:function(transactionId)
2699         {
2700             var obj,http;
2701             try
2702             {
2703
2704                 http = new XMLHttpRequest();
2705
2706                 obj = { conn:http, tId:transactionId };
2707             }
2708             catch(e)
2709             {
2710                 for (var i = 0; i < this.activeX.length; ++i) {
2711                     try
2712                     {
2713
2714                         http = new ActiveXObject(this.activeX[i]);
2715
2716                         obj = { conn:http, tId:transactionId };
2717                         break;
2718                     }
2719                     catch(e) {
2720                     }
2721                 }
2722             }
2723             finally
2724             {
2725                 return obj;
2726             }
2727         },
2728
2729         getConnectionObject:function()
2730         {
2731             var o;
2732             var tId = this.transactionId;
2733
2734             try
2735             {
2736                 o = this.createXhrObject(tId);
2737                 if (o) {
2738                     this.transactionId++;
2739                 }
2740             }
2741             catch(e) {
2742             }
2743             finally
2744             {
2745                 return o;
2746             }
2747         },
2748
2749         asyncRequest:function(method, uri, callback, postData)
2750         {
2751             var o = this.getConnectionObject();
2752
2753             if (!o) {
2754                 return null;
2755             }
2756             else {
2757                 o.conn.open(method, uri, true);
2758
2759                 if (this.useDefaultXhrHeader) {
2760                     if (!this.defaultHeaders['X-Requested-With']) {
2761                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2762                     }
2763                 }
2764
2765                 if(postData && this.useDefaultHeader){
2766                     this.initHeader('Content-Type', this.defaultPostHeader);
2767                 }
2768
2769                  if (this.hasDefaultHeaders || this.hasHeaders) {
2770                     this.setHeader(o);
2771                 }
2772
2773                 this.handleReadyState(o, callback);
2774                 o.conn.send(postData || null);
2775
2776                 return o;
2777             }
2778         },
2779
2780         handleReadyState:function(o, callback)
2781         {
2782             var oConn = this;
2783
2784             if (callback && callback.timeout) {
2785                 
2786                 this.timeout[o.tId] = window.setTimeout(function() {
2787                     oConn.abort(o, callback, true);
2788                 }, callback.timeout);
2789             }
2790
2791             this.poll[o.tId] = window.setInterval(
2792                     function() {
2793                         if (o.conn && o.conn.readyState == 4) {
2794                             window.clearInterval(oConn.poll[o.tId]);
2795                             delete oConn.poll[o.tId];
2796
2797                             if(callback && callback.timeout) {
2798                                 window.clearTimeout(oConn.timeout[o.tId]);
2799                                 delete oConn.timeout[o.tId];
2800                             }
2801
2802                             oConn.handleTransactionResponse(o, callback);
2803                         }
2804                     }
2805                     , this.pollInterval);
2806         },
2807
2808         handleTransactionResponse:function(o, callback, isAbort)
2809         {
2810
2811             if (!callback) {
2812                 this.releaseObject(o);
2813                 return;
2814             }
2815
2816             var httpStatus, responseObject;
2817
2818             try
2819             {
2820                 if (o.conn.status !== undefined && o.conn.status != 0) {
2821                     httpStatus = o.conn.status;
2822                 }
2823                 else {
2824                     httpStatus = 13030;
2825                 }
2826             }
2827             catch(e) {
2828
2829
2830                 httpStatus = 13030;
2831             }
2832
2833             if (httpStatus >= 200 && httpStatus < 300) {
2834                 responseObject = this.createResponseObject(o, callback.argument);
2835                 if (callback.success) {
2836                     if (!callback.scope) {
2837                         callback.success(responseObject);
2838                     }
2839                     else {
2840
2841
2842                         callback.success.apply(callback.scope, [responseObject]);
2843                     }
2844                 }
2845             }
2846             else {
2847                 switch (httpStatus) {
2848
2849                     case 12002:
2850                     case 12029:
2851                     case 12030:
2852                     case 12031:
2853                     case 12152:
2854                     case 13030:
2855                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2856                         if (callback.failure) {
2857                             if (!callback.scope) {
2858                                 callback.failure(responseObject);
2859                             }
2860                             else {
2861                                 callback.failure.apply(callback.scope, [responseObject]);
2862                             }
2863                         }
2864                         break;
2865                     default:
2866                         responseObject = this.createResponseObject(o, callback.argument);
2867                         if (callback.failure) {
2868                             if (!callback.scope) {
2869                                 callback.failure(responseObject);
2870                             }
2871                             else {
2872                                 callback.failure.apply(callback.scope, [responseObject]);
2873                             }
2874                         }
2875                 }
2876             }
2877
2878             this.releaseObject(o);
2879             responseObject = null;
2880         },
2881
2882         createResponseObject:function(o, callbackArg)
2883         {
2884             var obj = {};
2885             var headerObj = {};
2886
2887             try
2888             {
2889                 var headerStr = o.conn.getAllResponseHeaders();
2890                 var header = headerStr.split('\n');
2891                 for (var i = 0; i < header.length; i++) {
2892                     var delimitPos = header[i].indexOf(':');
2893                     if (delimitPos != -1) {
2894                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2895                     }
2896                 }
2897             }
2898             catch(e) {
2899             }
2900
2901             obj.tId = o.tId;
2902             obj.status = o.conn.status;
2903             obj.statusText = o.conn.statusText;
2904             obj.getResponseHeader = headerObj;
2905             obj.getAllResponseHeaders = headerStr;
2906             obj.responseText = o.conn.responseText;
2907             obj.responseXML = o.conn.responseXML;
2908
2909             if (typeof callbackArg !== undefined) {
2910                 obj.argument = callbackArg;
2911             }
2912
2913             return obj;
2914         },
2915
2916         createExceptionObject:function(tId, callbackArg, isAbort)
2917         {
2918             var COMM_CODE = 0;
2919             var COMM_ERROR = 'communication failure';
2920             var ABORT_CODE = -1;
2921             var ABORT_ERROR = 'transaction aborted';
2922
2923             var obj = {};
2924
2925             obj.tId = tId;
2926             if (isAbort) {
2927                 obj.status = ABORT_CODE;
2928                 obj.statusText = ABORT_ERROR;
2929             }
2930             else {
2931                 obj.status = COMM_CODE;
2932                 obj.statusText = COMM_ERROR;
2933             }
2934
2935             if (callbackArg) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         initHeader:function(label, value, isDefault)
2943         {
2944             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2945
2946             if (headerObj[label] === undefined) {
2947                 headerObj[label] = value;
2948             }
2949             else {
2950
2951
2952                 headerObj[label] = value + "," + headerObj[label];
2953             }
2954
2955             if (isDefault) {
2956                 this.hasDefaultHeaders = true;
2957             }
2958             else {
2959                 this.hasHeaders = true;
2960             }
2961         },
2962
2963
2964         setHeader:function(o)
2965         {
2966             if (this.hasDefaultHeaders) {
2967                 for (var prop in this.defaultHeaders) {
2968                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2969                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2970                     }
2971                 }
2972             }
2973
2974             if (this.hasHeaders) {
2975                 for (var prop in this.headers) {
2976                     if (this.headers.hasOwnProperty(prop)) {
2977                         o.conn.setRequestHeader(prop, this.headers[prop]);
2978                     }
2979                 }
2980                 this.headers = {};
2981                 this.hasHeaders = false;
2982             }
2983         },
2984
2985         resetDefaultHeaders:function() {
2986             delete this.defaultHeaders;
2987             this.defaultHeaders = {};
2988             this.hasDefaultHeaders = false;
2989         },
2990
2991         abort:function(o, callback, isTimeout)
2992         {
2993             if(this.isCallInProgress(o)) {
2994                 o.conn.abort();
2995                 window.clearInterval(this.poll[o.tId]);
2996                 delete this.poll[o.tId];
2997                 if (isTimeout) {
2998                     delete this.timeout[o.tId];
2999                 }
3000
3001                 this.handleTransactionResponse(o, callback, true);
3002
3003                 return true;
3004             }
3005             else {
3006                 return false;
3007             }
3008         },
3009
3010
3011         isCallInProgress:function(o)
3012         {
3013             if (o && o.conn) {
3014                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3015             }
3016             else {
3017
3018                 return false;
3019             }
3020         },
3021
3022
3023         releaseObject:function(o)
3024         {
3025
3026             o.conn = null;
3027
3028             o = null;
3029         },
3030
3031         activeX:[
3032         'MSXML2.XMLHTTP.3.0',
3033         'MSXML2.XMLHTTP',
3034         'Microsoft.XMLHTTP'
3035         ]
3036
3037
3038     };
3039 })();/*
3040  * Portions of this file are based on pieces of Yahoo User Interface Library
3041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3042  * YUI licensed under the BSD License:
3043  * http://developer.yahoo.net/yui/license.txt
3044  * <script type="text/javascript">
3045  *
3046  */
3047
3048 Roo.lib.Region = function(t, r, b, l) {
3049     this.top = t;
3050     this[1] = t;
3051     this.right = r;
3052     this.bottom = b;
3053     this.left = l;
3054     this[0] = l;
3055 };
3056
3057
3058 Roo.lib.Region.prototype = {
3059     contains : function(region) {
3060         return ( region.left >= this.left &&
3061                  region.right <= this.right &&
3062                  region.top >= this.top &&
3063                  region.bottom <= this.bottom    );
3064
3065     },
3066
3067     getArea : function() {
3068         return ( (this.bottom - this.top) * (this.right - this.left) );
3069     },
3070
3071     intersect : function(region) {
3072         var t = Math.max(this.top, region.top);
3073         var r = Math.min(this.right, region.right);
3074         var b = Math.min(this.bottom, region.bottom);
3075         var l = Math.max(this.left, region.left);
3076
3077         if (b >= t && r >= l) {
3078             return new Roo.lib.Region(t, r, b, l);
3079         } else {
3080             return null;
3081         }
3082     },
3083     union : function(region) {
3084         var t = Math.min(this.top, region.top);
3085         var r = Math.max(this.right, region.right);
3086         var b = Math.max(this.bottom, region.bottom);
3087         var l = Math.min(this.left, region.left);
3088
3089         return new Roo.lib.Region(t, r, b, l);
3090     },
3091
3092     adjust : function(t, l, b, r) {
3093         this.top += t;
3094         this.left += l;
3095         this.right += r;
3096         this.bottom += b;
3097         return this;
3098     }
3099 };
3100
3101 Roo.lib.Region.getRegion = function(el) {
3102     var p = Roo.lib.Dom.getXY(el);
3103
3104     var t = p[1];
3105     var r = p[0] + el.offsetWidth;
3106     var b = p[1] + el.offsetHeight;
3107     var l = p[0];
3108
3109     return new Roo.lib.Region(t, r, b, l);
3110 };
3111 /*
3112  * Portions of this file are based on pieces of Yahoo User Interface Library
3113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3114  * YUI licensed under the BSD License:
3115  * http://developer.yahoo.net/yui/license.txt
3116  * <script type="text/javascript">
3117  *
3118  */
3119 //@@dep Roo.lib.Region
3120
3121
3122 Roo.lib.Point = function(x, y) {
3123     if (x instanceof Array) {
3124         y = x[1];
3125         x = x[0];
3126     }
3127     this.x = this.right = this.left = this[0] = x;
3128     this.y = this.top = this.bottom = this[1] = y;
3129 };
3130
3131 Roo.lib.Point.prototype = new Roo.lib.Region();
3132 /*
3133  * Portions of this file are based on pieces of Yahoo User Interface Library
3134  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3135  * YUI licensed under the BSD License:
3136  * http://developer.yahoo.net/yui/license.txt
3137  * <script type="text/javascript">
3138  *
3139  */
3140  
3141 (function() {   
3142
3143     Roo.lib.Anim = {
3144         scroll : function(el, args, duration, easing, cb, scope) {
3145             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3146         },
3147
3148         motion : function(el, args, duration, easing, cb, scope) {
3149             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3150         },
3151
3152         color : function(el, args, duration, easing, cb, scope) {
3153             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3154         },
3155
3156         run : function(el, args, duration, easing, cb, scope, type) {
3157             type = type || Roo.lib.AnimBase;
3158             if (typeof easing == "string") {
3159                 easing = Roo.lib.Easing[easing];
3160             }
3161             var anim = new type(el, args, duration, easing);
3162             anim.animateX(function() {
3163                 Roo.callback(cb, scope);
3164             });
3165             return anim;
3166         }
3167     };
3168 })();/*
3169  * Portions of this file are based on pieces of Yahoo User Interface Library
3170  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3171  * YUI licensed under the BSD License:
3172  * http://developer.yahoo.net/yui/license.txt
3173  * <script type="text/javascript">
3174  *
3175  */
3176
3177 (function() {    
3178     var libFlyweight;
3179     
3180     function fly(el) {
3181         if (!libFlyweight) {
3182             libFlyweight = new Roo.Element.Flyweight();
3183         }
3184         libFlyweight.dom = el;
3185         return libFlyweight;
3186     }
3187
3188     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3189     
3190    
3191     
3192     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3193         if (el) {
3194             this.init(el, attributes, duration, method);
3195         }
3196     };
3197
3198     Roo.lib.AnimBase.fly = fly;
3199     
3200     
3201     
3202     Roo.lib.AnimBase.prototype = {
3203
3204         toString: function() {
3205             var el = this.getEl();
3206             var id = el.id || el.tagName;
3207             return ("Anim " + id);
3208         },
3209
3210         patterns: {
3211             noNegatives:        /width|height|opacity|padding/i,
3212             offsetAttribute:  /^((width|height)|(top|left))$/,
3213             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3214             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3215         },
3216
3217
3218         doMethod: function(attr, start, end) {
3219             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3220         },
3221
3222
3223         setAttribute: function(attr, val, unit) {
3224             if (this.patterns.noNegatives.test(attr)) {
3225                 val = (val > 0) ? val : 0;
3226             }
3227
3228             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3229         },
3230
3231
3232         getAttribute: function(attr) {
3233             var el = this.getEl();
3234             var val = fly(el).getStyle(attr);
3235
3236             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3237                 return parseFloat(val);
3238             }
3239
3240             var a = this.patterns.offsetAttribute.exec(attr) || [];
3241             var pos = !!( a[3] );
3242             var box = !!( a[2] );
3243
3244
3245             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3246                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3247             } else {
3248                 val = 0;
3249             }
3250
3251             return val;
3252         },
3253
3254
3255         getDefaultUnit: function(attr) {
3256             if (this.patterns.defaultUnit.test(attr)) {
3257                 return 'px';
3258             }
3259
3260             return '';
3261         },
3262
3263         animateX : function(callback, scope) {
3264             var f = function() {
3265                 this.onComplete.removeListener(f);
3266                 if (typeof callback == "function") {
3267                     callback.call(scope || this, this);
3268                 }
3269             };
3270             this.onComplete.addListener(f, this);
3271             this.animate();
3272         },
3273
3274
3275         setRuntimeAttribute: function(attr) {
3276             var start;
3277             var end;
3278             var attributes = this.attributes;
3279
3280             this.runtimeAttributes[attr] = {};
3281
3282             var isset = function(prop) {
3283                 return (typeof prop !== 'undefined');
3284             };
3285
3286             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3287                 return false;
3288             }
3289
3290             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3291
3292
3293             if (isset(attributes[attr]['to'])) {
3294                 end = attributes[attr]['to'];
3295             } else if (isset(attributes[attr]['by'])) {
3296                 if (start.constructor == Array) {
3297                     end = [];
3298                     for (var i = 0, len = start.length; i < len; ++i) {
3299                         end[i] = start[i] + attributes[attr]['by'][i];
3300                     }
3301                 } else {
3302                     end = start + attributes[attr]['by'];
3303                 }
3304             }
3305
3306             this.runtimeAttributes[attr].start = start;
3307             this.runtimeAttributes[attr].end = end;
3308
3309
3310             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3311         },
3312
3313
3314         init: function(el, attributes, duration, method) {
3315
3316             var isAnimated = false;
3317
3318
3319             var startTime = null;
3320
3321
3322             var actualFrames = 0;
3323
3324
3325             el = Roo.getDom(el);
3326
3327
3328             this.attributes = attributes || {};
3329
3330
3331             this.duration = duration || 1;
3332
3333
3334             this.method = method || Roo.lib.Easing.easeNone;
3335
3336
3337             this.useSeconds = true;
3338
3339
3340             this.currentFrame = 0;
3341
3342
3343             this.totalFrames = Roo.lib.AnimMgr.fps;
3344
3345
3346             this.getEl = function() {
3347                 return el;
3348             };
3349
3350
3351             this.isAnimated = function() {
3352                 return isAnimated;
3353             };
3354
3355
3356             this.getStartTime = function() {
3357                 return startTime;
3358             };
3359
3360             this.runtimeAttributes = {};
3361
3362
3363             this.animate = function() {
3364                 if (this.isAnimated()) {
3365                     return false;
3366                 }
3367
3368                 this.currentFrame = 0;
3369
3370                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3371
3372                 Roo.lib.AnimMgr.registerElement(this);
3373             };
3374
3375
3376             this.stop = function(finish) {
3377                 if (finish) {
3378                     this.currentFrame = this.totalFrames;
3379                     this._onTween.fire();
3380                 }
3381                 Roo.lib.AnimMgr.stop(this);
3382             };
3383
3384             var onStart = function() {
3385                 this.onStart.fire();
3386
3387                 this.runtimeAttributes = {};
3388                 for (var attr in this.attributes) {
3389                     this.setRuntimeAttribute(attr);
3390                 }
3391
3392                 isAnimated = true;
3393                 actualFrames = 0;
3394                 startTime = new Date();
3395             };
3396
3397
3398             var onTween = function() {
3399                 var data = {
3400                     duration: new Date() - this.getStartTime(),
3401                     currentFrame: this.currentFrame
3402                 };
3403
3404                 data.toString = function() {
3405                     return (
3406                             'duration: ' + data.duration +
3407                             ', currentFrame: ' + data.currentFrame
3408                             );
3409                 };
3410
3411                 this.onTween.fire(data);
3412
3413                 var runtimeAttributes = this.runtimeAttributes;
3414
3415                 for (var attr in runtimeAttributes) {
3416                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3417                 }
3418
3419                 actualFrames += 1;
3420             };
3421
3422             var onComplete = function() {
3423                 var actual_duration = (new Date() - startTime) / 1000 ;
3424
3425                 var data = {
3426                     duration: actual_duration,
3427                     frames: actualFrames,
3428                     fps: actualFrames / actual_duration
3429                 };
3430
3431                 data.toString = function() {
3432                     return (
3433                             'duration: ' + data.duration +
3434                             ', frames: ' + data.frames +
3435                             ', fps: ' + data.fps
3436                             );
3437                 };
3438
3439                 isAnimated = false;
3440                 actualFrames = 0;
3441                 this.onComplete.fire(data);
3442             };
3443
3444
3445             this._onStart = new Roo.util.Event(this);
3446             this.onStart = new Roo.util.Event(this);
3447             this.onTween = new Roo.util.Event(this);
3448             this._onTween = new Roo.util.Event(this);
3449             this.onComplete = new Roo.util.Event(this);
3450             this._onComplete = new Roo.util.Event(this);
3451             this._onStart.addListener(onStart);
3452             this._onTween.addListener(onTween);
3453             this._onComplete.addListener(onComplete);
3454         }
3455     };
3456 })();
3457 /*
3458  * Portions of this file are based on pieces of Yahoo User Interface Library
3459  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3460  * YUI licensed under the BSD License:
3461  * http://developer.yahoo.net/yui/license.txt
3462  * <script type="text/javascript">
3463  *
3464  */
3465
3466 Roo.lib.AnimMgr = new function() {
3467
3468     var thread = null;
3469
3470
3471     var queue = [];
3472
3473
3474     var tweenCount = 0;
3475
3476
3477     this.fps = 1000;
3478
3479
3480     this.delay = 1;
3481
3482
3483     this.registerElement = function(tween) {
3484         queue[queue.length] = tween;
3485         tweenCount += 1;
3486         tween._onStart.fire();
3487         this.start();
3488     };
3489
3490
3491     this.unRegister = function(tween, index) {
3492         tween._onComplete.fire();
3493         index = index || getIndex(tween);
3494         if (index != -1) {
3495             queue.splice(index, 1);
3496         }
3497
3498         tweenCount -= 1;
3499         if (tweenCount <= 0) {
3500             this.stop();
3501         }
3502     };
3503
3504
3505     this.start = function() {
3506         if (thread === null) {
3507             thread = setInterval(this.run, this.delay);
3508         }
3509     };
3510
3511
3512     this.stop = function(tween) {
3513         if (!tween) {
3514             clearInterval(thread);
3515
3516             for (var i = 0, len = queue.length; i < len; ++i) {
3517                 if (queue[0].isAnimated()) {
3518                     this.unRegister(queue[0], 0);
3519                 }
3520             }
3521
3522             queue = [];
3523             thread = null;
3524             tweenCount = 0;
3525         }
3526         else {
3527             this.unRegister(tween);
3528         }
3529     };
3530
3531
3532     this.run = function() {
3533         for (var i = 0, len = queue.length; i < len; ++i) {
3534             var tween = queue[i];
3535             if (!tween || !tween.isAnimated()) {
3536                 continue;
3537             }
3538
3539             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3540             {
3541                 tween.currentFrame += 1;
3542
3543                 if (tween.useSeconds) {
3544                     correctFrame(tween);
3545                 }
3546                 tween._onTween.fire();
3547             }
3548             else {
3549                 Roo.lib.AnimMgr.stop(tween, i);
3550             }
3551         }
3552     };
3553
3554     var getIndex = function(anim) {
3555         for (var i = 0, len = queue.length; i < len; ++i) {
3556             if (queue[i] == anim) {
3557                 return i;
3558             }
3559         }
3560         return -1;
3561     };
3562
3563
3564     var correctFrame = function(tween) {
3565         var frames = tween.totalFrames;
3566         var frame = tween.currentFrame;
3567         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3568         var elapsed = (new Date() - tween.getStartTime());
3569         var tweak = 0;
3570
3571         if (elapsed < tween.duration * 1000) {
3572             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3573         } else {
3574             tweak = frames - (frame + 1);
3575         }
3576         if (tweak > 0 && isFinite(tweak)) {
3577             if (tween.currentFrame + tweak >= frames) {
3578                 tweak = frames - (frame + 1);
3579             }
3580
3581             tween.currentFrame += tweak;
3582         }
3583     };
3584 };
3585
3586     /*
3587  * Portions of this file are based on pieces of Yahoo User Interface Library
3588  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3589  * YUI licensed under the BSD License:
3590  * http://developer.yahoo.net/yui/license.txt
3591  * <script type="text/javascript">
3592  *
3593  */
3594 Roo.lib.Bezier = new function() {
3595
3596         this.getPosition = function(points, t) {
3597             var n = points.length;
3598             var tmp = [];
3599
3600             for (var i = 0; i < n; ++i) {
3601                 tmp[i] = [points[i][0], points[i][1]];
3602             }
3603
3604             for (var j = 1; j < n; ++j) {
3605                 for (i = 0; i < n - j; ++i) {
3606                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3607                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3608                 }
3609             }
3610
3611             return [ tmp[0][0], tmp[0][1] ];
3612
3613         };
3614     };/*
3615  * Portions of this file are based on pieces of Yahoo User Interface Library
3616  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3617  * YUI licensed under the BSD License:
3618  * http://developer.yahoo.net/yui/license.txt
3619  * <script type="text/javascript">
3620  *
3621  */
3622 (function() {
3623
3624     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3625         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3626     };
3627
3628     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3629
3630     var fly = Roo.lib.AnimBase.fly;
3631     var Y = Roo.lib;
3632     var superclass = Y.ColorAnim.superclass;
3633     var proto = Y.ColorAnim.prototype;
3634
3635     proto.toString = function() {
3636         var el = this.getEl();
3637         var id = el.id || el.tagName;
3638         return ("ColorAnim " + id);
3639     };
3640
3641     proto.patterns.color = /color$/i;
3642     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3643     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3644     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3645     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3646
3647
3648     proto.parseColor = function(s) {
3649         if (s.length == 3) {
3650             return s;
3651         }
3652
3653         var c = this.patterns.hex.exec(s);
3654         if (c && c.length == 4) {
3655             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3656         }
3657
3658         c = this.patterns.rgb.exec(s);
3659         if (c && c.length == 4) {
3660             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3661         }
3662
3663         c = this.patterns.hex3.exec(s);
3664         if (c && c.length == 4) {
3665             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3666         }
3667
3668         return null;
3669     };
3670     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3671     proto.getAttribute = function(attr) {
3672         var el = this.getEl();
3673         if (this.patterns.color.test(attr)) {
3674             var val = fly(el).getStyle(attr);
3675
3676             if (this.patterns.transparent.test(val)) {
3677                 var parent = el.parentNode;
3678                 val = fly(parent).getStyle(attr);
3679
3680                 while (parent && this.patterns.transparent.test(val)) {
3681                     parent = parent.parentNode;
3682                     val = fly(parent).getStyle(attr);
3683                     if (parent.tagName.toUpperCase() == 'HTML') {
3684                         val = '#fff';
3685                     }
3686                 }
3687             }
3688         } else {
3689             val = superclass.getAttribute.call(this, attr);
3690         }
3691
3692         return val;
3693     };
3694     proto.getAttribute = function(attr) {
3695         var el = this.getEl();
3696         if (this.patterns.color.test(attr)) {
3697             var val = fly(el).getStyle(attr);
3698
3699             if (this.patterns.transparent.test(val)) {
3700                 var parent = el.parentNode;
3701                 val = fly(parent).getStyle(attr);
3702
3703                 while (parent && this.patterns.transparent.test(val)) {
3704                     parent = parent.parentNode;
3705                     val = fly(parent).getStyle(attr);
3706                     if (parent.tagName.toUpperCase() == 'HTML') {
3707                         val = '#fff';
3708                     }
3709                 }
3710             }
3711         } else {
3712             val = superclass.getAttribute.call(this, attr);
3713         }
3714
3715         return val;
3716     };
3717
3718     proto.doMethod = function(attr, start, end) {
3719         var val;
3720
3721         if (this.patterns.color.test(attr)) {
3722             val = [];
3723             for (var i = 0, len = start.length; i < len; ++i) {
3724                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3725             }
3726
3727             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3728         }
3729         else {
3730             val = superclass.doMethod.call(this, attr, start, end);
3731         }
3732
3733         return val;
3734     };
3735
3736     proto.setRuntimeAttribute = function(attr) {
3737         superclass.setRuntimeAttribute.call(this, attr);
3738
3739         if (this.patterns.color.test(attr)) {
3740             var attributes = this.attributes;
3741             var start = this.parseColor(this.runtimeAttributes[attr].start);
3742             var end = this.parseColor(this.runtimeAttributes[attr].end);
3743
3744             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3745                 end = this.parseColor(attributes[attr].by);
3746
3747                 for (var i = 0, len = start.length; i < len; ++i) {
3748                     end[i] = start[i] + end[i];
3749                 }
3750             }
3751
3752             this.runtimeAttributes[attr].start = start;
3753             this.runtimeAttributes[attr].end = end;
3754         }
3755     };
3756 })();
3757
3758 /*
3759  * Portions of this file are based on pieces of Yahoo User Interface Library
3760  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3761  * YUI licensed under the BSD License:
3762  * http://developer.yahoo.net/yui/license.txt
3763  * <script type="text/javascript">
3764  *
3765  */
3766 Roo.lib.Easing = {
3767
3768
3769     easeNone: function (t, b, c, d) {
3770         return c * t / d + b;
3771     },
3772
3773
3774     easeIn: function (t, b, c, d) {
3775         return c * (t /= d) * t + b;
3776     },
3777
3778
3779     easeOut: function (t, b, c, d) {
3780         return -c * (t /= d) * (t - 2) + b;
3781     },
3782
3783
3784     easeBoth: function (t, b, c, d) {
3785         if ((t /= d / 2) < 1) {
3786             return c / 2 * t * t + b;
3787         }
3788
3789         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3790     },
3791
3792
3793     easeInStrong: function (t, b, c, d) {
3794         return c * (t /= d) * t * t * t + b;
3795     },
3796
3797
3798     easeOutStrong: function (t, b, c, d) {
3799         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3800     },
3801
3802
3803     easeBothStrong: function (t, b, c, d) {
3804         if ((t /= d / 2) < 1) {
3805             return c / 2 * t * t * t * t + b;
3806         }
3807
3808         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3809     },
3810
3811
3812
3813     elasticIn: function (t, b, c, d, a, p) {
3814         if (t == 0) {
3815             return b;
3816         }
3817         if ((t /= d) == 1) {
3818             return b + c;
3819         }
3820         if (!p) {
3821             p = d * .3;
3822         }
3823
3824         if (!a || a < Math.abs(c)) {
3825             a = c;
3826             var s = p / 4;
3827         }
3828         else {
3829             var s = p / (2 * Math.PI) * Math.asin(c / a);
3830         }
3831
3832         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3833     },
3834
3835
3836     elasticOut: function (t, b, c, d, a, p) {
3837         if (t == 0) {
3838             return b;
3839         }
3840         if ((t /= d) == 1) {
3841             return b + c;
3842         }
3843         if (!p) {
3844             p = d * .3;
3845         }
3846
3847         if (!a || a < Math.abs(c)) {
3848             a = c;
3849             var s = p / 4;
3850         }
3851         else {
3852             var s = p / (2 * Math.PI) * Math.asin(c / a);
3853         }
3854
3855         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3856     },
3857
3858
3859     elasticBoth: function (t, b, c, d, a, p) {
3860         if (t == 0) {
3861             return b;
3862         }
3863
3864         if ((t /= d / 2) == 2) {
3865             return b + c;
3866         }
3867
3868         if (!p) {
3869             p = d * (.3 * 1.5);
3870         }
3871
3872         if (!a || a < Math.abs(c)) {
3873             a = c;
3874             var s = p / 4;
3875         }
3876         else {
3877             var s = p / (2 * Math.PI) * Math.asin(c / a);
3878         }
3879
3880         if (t < 1) {
3881             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3882                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3883         }
3884         return a * Math.pow(2, -10 * (t -= 1)) *
3885                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3886     },
3887
3888
3889
3890     backIn: function (t, b, c, d, s) {
3891         if (typeof s == 'undefined') {
3892             s = 1.70158;
3893         }
3894         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3895     },
3896
3897
3898     backOut: function (t, b, c, d, s) {
3899         if (typeof s == 'undefined') {
3900             s = 1.70158;
3901         }
3902         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3903     },
3904
3905
3906     backBoth: function (t, b, c, d, s) {
3907         if (typeof s == 'undefined') {
3908             s = 1.70158;
3909         }
3910
3911         if ((t /= d / 2 ) < 1) {
3912             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3913         }
3914         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3915     },
3916
3917
3918     bounceIn: function (t, b, c, d) {
3919         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3920     },
3921
3922
3923     bounceOut: function (t, b, c, d) {
3924         if ((t /= d) < (1 / 2.75)) {
3925             return c * (7.5625 * t * t) + b;
3926         } else if (t < (2 / 2.75)) {
3927             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3928         } else if (t < (2.5 / 2.75)) {
3929             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3930         }
3931         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3932     },
3933
3934
3935     bounceBoth: function (t, b, c, d) {
3936         if (t < d / 2) {
3937             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3938         }
3939         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3940     }
3941 };/*
3942  * Portions of this file are based on pieces of Yahoo User Interface Library
3943  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3944  * YUI licensed under the BSD License:
3945  * http://developer.yahoo.net/yui/license.txt
3946  * <script type="text/javascript">
3947  *
3948  */
3949     (function() {
3950         Roo.lib.Motion = function(el, attributes, duration, method) {
3951             if (el) {
3952                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3953             }
3954         };
3955
3956         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3957
3958
3959         var Y = Roo.lib;
3960         var superclass = Y.Motion.superclass;
3961         var proto = Y.Motion.prototype;
3962
3963         proto.toString = function() {
3964             var el = this.getEl();
3965             var id = el.id || el.tagName;
3966             return ("Motion " + id);
3967         };
3968
3969         proto.patterns.points = /^points$/i;
3970
3971         proto.setAttribute = function(attr, val, unit) {
3972             if (this.patterns.points.test(attr)) {
3973                 unit = unit || 'px';
3974                 superclass.setAttribute.call(this, 'left', val[0], unit);
3975                 superclass.setAttribute.call(this, 'top', val[1], unit);
3976             } else {
3977                 superclass.setAttribute.call(this, attr, val, unit);
3978             }
3979         };
3980
3981         proto.getAttribute = function(attr) {
3982             if (this.patterns.points.test(attr)) {
3983                 var val = [
3984                         superclass.getAttribute.call(this, 'left'),
3985                         superclass.getAttribute.call(this, 'top')
3986                         ];
3987             } else {
3988                 val = superclass.getAttribute.call(this, attr);
3989             }
3990
3991             return val;
3992         };
3993
3994         proto.doMethod = function(attr, start, end) {
3995             var val = null;
3996
3997             if (this.patterns.points.test(attr)) {
3998                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3999                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4000             } else {
4001                 val = superclass.doMethod.call(this, attr, start, end);
4002             }
4003             return val;
4004         };
4005
4006         proto.setRuntimeAttribute = function(attr) {
4007             if (this.patterns.points.test(attr)) {
4008                 var el = this.getEl();
4009                 var attributes = this.attributes;
4010                 var start;
4011                 var control = attributes['points']['control'] || [];
4012                 var end;
4013                 var i, len;
4014
4015                 if (control.length > 0 && !(control[0] instanceof Array)) {
4016                     control = [control];
4017                 } else {
4018                     var tmp = [];
4019                     for (i = 0,len = control.length; i < len; ++i) {
4020                         tmp[i] = control[i];
4021                     }
4022                     control = tmp;
4023                 }
4024
4025                 Roo.fly(el).position();
4026
4027                 if (isset(attributes['points']['from'])) {
4028                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4029                 }
4030                 else {
4031                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4032                 }
4033
4034                 start = this.getAttribute('points');
4035
4036
4037                 if (isset(attributes['points']['to'])) {
4038                     end = translateValues.call(this, attributes['points']['to'], start);
4039
4040                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4041                     for (i = 0,len = control.length; i < len; ++i) {
4042                         control[i] = translateValues.call(this, control[i], start);
4043                     }
4044
4045
4046                 } else if (isset(attributes['points']['by'])) {
4047                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4048
4049                     for (i = 0,len = control.length; i < len; ++i) {
4050                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4051                     }
4052                 }
4053
4054                 this.runtimeAttributes[attr] = [start];
4055
4056                 if (control.length > 0) {
4057                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4058                 }
4059
4060                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4061             }
4062             else {
4063                 superclass.setRuntimeAttribute.call(this, attr);
4064             }
4065         };
4066
4067         var translateValues = function(val, start) {
4068             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4069             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4070
4071             return val;
4072         };
4073
4074         var isset = function(prop) {
4075             return (typeof prop !== 'undefined');
4076         };
4077     })();
4078 /*
4079  * Portions of this file are based on pieces of Yahoo User Interface Library
4080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4081  * YUI licensed under the BSD License:
4082  * http://developer.yahoo.net/yui/license.txt
4083  * <script type="text/javascript">
4084  *
4085  */
4086     (function() {
4087         Roo.lib.Scroll = function(el, attributes, duration, method) {
4088             if (el) {
4089                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4090             }
4091         };
4092
4093         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4094
4095
4096         var Y = Roo.lib;
4097         var superclass = Y.Scroll.superclass;
4098         var proto = Y.Scroll.prototype;
4099
4100         proto.toString = function() {
4101             var el = this.getEl();
4102             var id = el.id || el.tagName;
4103             return ("Scroll " + id);
4104         };
4105
4106         proto.doMethod = function(attr, start, end) {
4107             var val = null;
4108
4109             if (attr == 'scroll') {
4110                 val = [
4111                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4112                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4113                         ];
4114
4115             } else {
4116                 val = superclass.doMethod.call(this, attr, start, end);
4117             }
4118             return val;
4119         };
4120
4121         proto.getAttribute = function(attr) {
4122             var val = null;
4123             var el = this.getEl();
4124
4125             if (attr == 'scroll') {
4126                 val = [ el.scrollLeft, el.scrollTop ];
4127             } else {
4128                 val = superclass.getAttribute.call(this, attr);
4129             }
4130
4131             return val;
4132         };
4133
4134         proto.setAttribute = function(attr, val, unit) {
4135             var el = this.getEl();
4136
4137             if (attr == 'scroll') {
4138                 el.scrollLeft = val[0];
4139                 el.scrollTop = val[1];
4140             } else {
4141                 superclass.setAttribute.call(this, attr, val, unit);
4142             }
4143         };
4144     })();
4145 /*
4146  * Based on:
4147  * Ext JS Library 1.1.1
4148  * Copyright(c) 2006-2007, Ext JS, LLC.
4149  *
4150  * Originally Released Under LGPL - original licence link has changed is not relivant.
4151  *
4152  * Fork - LGPL
4153  * <script type="text/javascript">
4154  */
4155
4156
4157 // nasty IE9 hack - what a pile of crap that is..
4158
4159  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4160     Range.prototype.createContextualFragment = function (html) {
4161         var doc = window.document;
4162         var container = doc.createElement("div");
4163         container.innerHTML = html;
4164         var frag = doc.createDocumentFragment(), n;
4165         while ((n = container.firstChild)) {
4166             frag.appendChild(n);
4167         }
4168         return frag;
4169     };
4170 }
4171
4172 /**
4173  * @class Roo.DomHelper
4174  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4175  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4176  * @singleton
4177  */
4178 Roo.DomHelper = function(){
4179     var tempTableEl = null;
4180     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4181     var tableRe = /^table|tbody|tr|td$/i;
4182     var xmlns = {};
4183     // build as innerHTML where available
4184     /** @ignore */
4185     var createHtml = function(o){
4186         if(typeof o == 'string'){
4187             return o;
4188         }
4189         var b = "";
4190         if(!o.tag){
4191             o.tag = "div";
4192         }
4193         b += "<" + o.tag;
4194         for(var attr in o){
4195             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4196             if(attr == "style"){
4197                 var s = o["style"];
4198                 if(typeof s == "function"){
4199                     s = s.call();
4200                 }
4201                 if(typeof s == "string"){
4202                     b += ' style="' + s + '"';
4203                 }else if(typeof s == "object"){
4204                     b += ' style="';
4205                     for(var key in s){
4206                         if(typeof s[key] != "function"){
4207                             b += key + ":" + s[key] + ";";
4208                         }
4209                     }
4210                     b += '"';
4211                 }
4212             }else{
4213                 if(attr == "cls"){
4214                     b += ' class="' + o["cls"] + '"';
4215                 }else if(attr == "htmlFor"){
4216                     b += ' for="' + o["htmlFor"] + '"';
4217                 }else{
4218                     b += " " + attr + '="' + o[attr] + '"';
4219                 }
4220             }
4221         }
4222         if(emptyTags.test(o.tag)){
4223             b += "/>";
4224         }else{
4225             b += ">";
4226             var cn = o.children || o.cn;
4227             if(cn){
4228                 //http://bugs.kde.org/show_bug.cgi?id=71506
4229                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4230                     for(var i = 0, len = cn.length; i < len; i++) {
4231                         b += createHtml(cn[i], b);
4232                     }
4233                 }else{
4234                     b += createHtml(cn, b);
4235                 }
4236             }
4237             if(o.html){
4238                 b += o.html;
4239             }
4240             b += "</" + o.tag + ">";
4241         }
4242         return b;
4243     };
4244
4245     // build as dom
4246     /** @ignore */
4247     var createDom = function(o, parentNode){
4248          
4249         // defininition craeted..
4250         var ns = false;
4251         if (o.ns && o.ns != 'html') {
4252                
4253             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4254                 xmlns[o.ns] = o.xmlns;
4255                 ns = o.xmlns;
4256             }
4257             if (typeof(xmlns[o.ns]) == 'undefined') {
4258                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4259             }
4260             ns = xmlns[o.ns];
4261         }
4262         
4263         
4264         if (typeof(o) == 'string') {
4265             return parentNode.appendChild(document.createTextNode(o));
4266         }
4267         o.tag = o.tag || div;
4268         if (o.ns && Roo.isIE) {
4269             ns = false;
4270             o.tag = o.ns + ':' + o.tag;
4271             
4272         }
4273         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4274         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4275         for(var attr in o){
4276             
4277             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4278                     attr == "style" || typeof o[attr] == "function") { continue; }
4279                     
4280             if(attr=="cls" && Roo.isIE){
4281                 el.className = o["cls"];
4282             }else{
4283                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4284                 else { 
4285                     el[attr] = o[attr];
4286                 }
4287             }
4288         }
4289         Roo.DomHelper.applyStyles(el, o.style);
4290         var cn = o.children || o.cn;
4291         if(cn){
4292             //http://bugs.kde.org/show_bug.cgi?id=71506
4293              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4294                 for(var i = 0, len = cn.length; i < len; i++) {
4295                     createDom(cn[i], el);
4296                 }
4297             }else{
4298                 createDom(cn, el);
4299             }
4300         }
4301         if(o.html){
4302             el.innerHTML = o.html;
4303         }
4304         if(parentNode){
4305            parentNode.appendChild(el);
4306         }
4307         return el;
4308     };
4309
4310     var ieTable = function(depth, s, h, e){
4311         tempTableEl.innerHTML = [s, h, e].join('');
4312         var i = -1, el = tempTableEl;
4313         while(++i < depth){
4314             el = el.firstChild;
4315         }
4316         return el;
4317     };
4318
4319     // kill repeat to save bytes
4320     var ts = '<table>',
4321         te = '</table>',
4322         tbs = ts+'<tbody>',
4323         tbe = '</tbody>'+te,
4324         trs = tbs + '<tr>',
4325         tre = '</tr>'+tbe;
4326
4327     /**
4328      * @ignore
4329      * Nasty code for IE's broken table implementation
4330      */
4331     var insertIntoTable = function(tag, where, el, html){
4332         if(!tempTableEl){
4333             tempTableEl = document.createElement('div');
4334         }
4335         var node;
4336         var before = null;
4337         if(tag == 'td'){
4338             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4339                 return;
4340             }
4341             if(where == 'beforebegin'){
4342                 before = el;
4343                 el = el.parentNode;
4344             } else{
4345                 before = el.nextSibling;
4346                 el = el.parentNode;
4347             }
4348             node = ieTable(4, trs, html, tre);
4349         }
4350         else if(tag == 'tr'){
4351             if(where == 'beforebegin'){
4352                 before = el;
4353                 el = el.parentNode;
4354                 node = ieTable(3, tbs, html, tbe);
4355             } else if(where == 'afterend'){
4356                 before = el.nextSibling;
4357                 el = el.parentNode;
4358                 node = ieTable(3, tbs, html, tbe);
4359             } else{ // INTO a TR
4360                 if(where == 'afterbegin'){
4361                     before = el.firstChild;
4362                 }
4363                 node = ieTable(4, trs, html, tre);
4364             }
4365         } else if(tag == 'tbody'){
4366             if(where == 'beforebegin'){
4367                 before = el;
4368                 el = el.parentNode;
4369                 node = ieTable(2, ts, html, te);
4370             } else if(where == 'afterend'){
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373                 node = ieTable(2, ts, html, te);
4374             } else{
4375                 if(where == 'afterbegin'){
4376                     before = el.firstChild;
4377                 }
4378                 node = ieTable(3, tbs, html, tbe);
4379             }
4380         } else{ // TABLE
4381             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4382                 return;
4383             }
4384             if(where == 'afterbegin'){
4385                 before = el.firstChild;
4386             }
4387             node = ieTable(2, ts, html, te);
4388         }
4389         el.insertBefore(node, before);
4390         return node;
4391     };
4392
4393     return {
4394     /** True to force the use of DOM instead of html fragments @type Boolean */
4395     useDom : false,
4396
4397     /**
4398      * Returns the markup for the passed Element(s) config
4399      * @param {Object} o The Dom object spec (and children)
4400      * @return {String}
4401      */
4402     markup : function(o){
4403         return createHtml(o);
4404     },
4405
4406     /**
4407      * Applies a style specification to an element
4408      * @param {String/HTMLElement} el The element to apply styles to
4409      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4410      * a function which returns such a specification.
4411      */
4412     applyStyles : function(el, styles){
4413         if(styles){
4414            el = Roo.fly(el);
4415            if(typeof styles == "string"){
4416                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4417                var matches;
4418                while ((matches = re.exec(styles)) != null){
4419                    el.setStyle(matches[1], matches[2]);
4420                }
4421            }else if (typeof styles == "object"){
4422                for (var style in styles){
4423                   el.setStyle(style, styles[style]);
4424                }
4425            }else if (typeof styles == "function"){
4426                 Roo.DomHelper.applyStyles(el, styles.call());
4427            }
4428         }
4429     },
4430
4431     /**
4432      * Inserts an HTML fragment into the Dom
4433      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4434      * @param {HTMLElement} el The context element
4435      * @param {String} html The HTML fragmenet
4436      * @return {HTMLElement} The new node
4437      */
4438     insertHtml : function(where, el, html){
4439         where = where.toLowerCase();
4440         if(el.insertAdjacentHTML){
4441             if(tableRe.test(el.tagName)){
4442                 var rs;
4443                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4444                     return rs;
4445                 }
4446             }
4447             switch(where){
4448                 case "beforebegin":
4449                     el.insertAdjacentHTML('BeforeBegin', html);
4450                     return el.previousSibling;
4451                 case "afterbegin":
4452                     el.insertAdjacentHTML('AfterBegin', html);
4453                     return el.firstChild;
4454                 case "beforeend":
4455                     el.insertAdjacentHTML('BeforeEnd', html);
4456                     return el.lastChild;
4457                 case "afterend":
4458                     el.insertAdjacentHTML('AfterEnd', html);
4459                     return el.nextSibling;
4460             }
4461             throw 'Illegal insertion point -> "' + where + '"';
4462         }
4463         var range = el.ownerDocument.createRange();
4464         var frag;
4465         switch(where){
4466              case "beforebegin":
4467                 range.setStartBefore(el);
4468                 frag = range.createContextualFragment(html);
4469                 el.parentNode.insertBefore(frag, el);
4470                 return el.previousSibling;
4471              case "afterbegin":
4472                 if(el.firstChild){
4473                     range.setStartBefore(el.firstChild);
4474                     frag = range.createContextualFragment(html);
4475                     el.insertBefore(frag, el.firstChild);
4476                     return el.firstChild;
4477                 }else{
4478                     el.innerHTML = html;
4479                     return el.firstChild;
4480                 }
4481             case "beforeend":
4482                 if(el.lastChild){
4483                     range.setStartAfter(el.lastChild);
4484                     frag = range.createContextualFragment(html);
4485                     el.appendChild(frag);
4486                     return el.lastChild;
4487                 }else{
4488                     el.innerHTML = html;
4489                     return el.lastChild;
4490                 }
4491             case "afterend":
4492                 range.setStartAfter(el);
4493                 frag = range.createContextualFragment(html);
4494                 el.parentNode.insertBefore(frag, el.nextSibling);
4495                 return el.nextSibling;
4496             }
4497             throw 'Illegal insertion point -> "' + where + '"';
4498     },
4499
4500     /**
4501      * Creates new Dom element(s) and inserts them before el
4502      * @param {String/HTMLElement/Element} el The context element
4503      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4504      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4505      * @return {HTMLElement/Roo.Element} The new node
4506      */
4507     insertBefore : function(el, o, returnElement){
4508         return this.doInsert(el, o, returnElement, "beforeBegin");
4509     },
4510
4511     /**
4512      * Creates new Dom element(s) and inserts them after el
4513      * @param {String/HTMLElement/Element} el The context element
4514      * @param {Object} o The Dom object spec (and children)
4515      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4516      * @return {HTMLElement/Roo.Element} The new node
4517      */
4518     insertAfter : function(el, o, returnElement){
4519         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4520     },
4521
4522     /**
4523      * Creates new Dom element(s) and inserts them as the first child of el
4524      * @param {String/HTMLElement/Element} el The context element
4525      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4526      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4527      * @return {HTMLElement/Roo.Element} The new node
4528      */
4529     insertFirst : function(el, o, returnElement){
4530         return this.doInsert(el, o, returnElement, "afterBegin");
4531     },
4532
4533     // private
4534     doInsert : function(el, o, returnElement, pos, sibling){
4535         el = Roo.getDom(el);
4536         var newNode;
4537         if(this.useDom || o.ns){
4538             newNode = createDom(o, null);
4539             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4540         }else{
4541             var html = createHtml(o);
4542             newNode = this.insertHtml(pos, el, html);
4543         }
4544         return returnElement ? Roo.get(newNode, true) : newNode;
4545     },
4546
4547     /**
4548      * Creates new Dom element(s) and appends them to el
4549      * @param {String/HTMLElement/Element} el The context element
4550      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4551      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4552      * @return {HTMLElement/Roo.Element} The new node
4553      */
4554     append : function(el, o, returnElement){
4555         el = Roo.getDom(el);
4556         var newNode;
4557         if(this.useDom || o.ns){
4558             newNode = createDom(o, null);
4559             el.appendChild(newNode);
4560         }else{
4561             var html = createHtml(o);
4562             newNode = this.insertHtml("beforeEnd", el, html);
4563         }
4564         return returnElement ? Roo.get(newNode, true) : newNode;
4565     },
4566
4567     /**
4568      * Creates new Dom element(s) and overwrites the contents of el with them
4569      * @param {String/HTMLElement/Element} el The context element
4570      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4571      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4572      * @return {HTMLElement/Roo.Element} The new node
4573      */
4574     overwrite : function(el, o, returnElement){
4575         el = Roo.getDom(el);
4576         if (o.ns) {
4577           
4578             while (el.childNodes.length) {
4579                 el.removeChild(el.firstChild);
4580             }
4581             createDom(o, el);
4582         } else {
4583             el.innerHTML = createHtml(o);   
4584         }
4585         
4586         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4587     },
4588
4589     /**
4590      * Creates a new Roo.DomHelper.Template from the Dom object spec
4591      * @param {Object} o The Dom object spec (and children)
4592      * @return {Roo.DomHelper.Template} The new template
4593      */
4594     createTemplate : function(o){
4595         var html = createHtml(o);
4596         return new Roo.Template(html);
4597     }
4598     };
4599 }();
4600 /*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611 /**
4612 * @class Roo.Template
4613 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4614 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4615 * Usage:
4616 <pre><code>
4617 var t = new Roo.Template({
4618     html :  '&lt;div name="{id}"&gt;' + 
4619         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4620         '&lt;/div&gt;',
4621     myformat: function (value, allValues) {
4622         return 'XX' + value;
4623     }
4624 });
4625 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4626 </code></pre>
4627 * For more information see this blog post with examples:
4628 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4629      - Create Elements using DOM, HTML fragments and Templates</a>. 
4630 * @constructor
4631 * @param {Object} cfg - Configuration object.
4632 */
4633 Roo.Template = function(cfg){
4634     // BC!
4635     if(cfg instanceof Array){
4636         cfg = cfg.join("");
4637     }else if(arguments.length > 1){
4638         cfg = Array.prototype.join.call(arguments, "");
4639     }
4640     
4641     
4642     if (typeof(cfg) == 'object') {
4643         Roo.apply(this,cfg)
4644     } else {
4645         // bc
4646         this.html = cfg;
4647     }
4648     if (this.url) {
4649         this.load();
4650     }
4651     
4652 };
4653 Roo.Template.prototype = {
4654     
4655     /**
4656      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4657      *                    it should be fixed so that template is observable...
4658      */
4659     url : false,
4660     /**
4661      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4662      */
4663     html : '',
4664     /**
4665      * Returns an HTML fragment of this template with the specified values applied.
4666      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4667      * @return {String} The HTML fragment
4668      */
4669     applyTemplate : function(values){
4670         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                 
6078                 for(var i = 0; i < len; i++){
6079                     var args = Array.prototype.slice.call(arguments, 0);
6080                     var l = ls[i];
6081                     args.push(l.options);
6082                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6083                         this.firing = false;
6084                         return false;
6085                     }
6086                 }
6087                 this.firing = false;
6088             }
6089             return true;
6090         }
6091     };
6092 })();/*
6093  * RooJS Library 
6094  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6095  *
6096  * Licence LGPL 
6097  *
6098  */
6099  
6100 /**
6101  * @class Roo.Document
6102  * @extends Roo.util.Observable
6103  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6104  * 
6105  * @param {Object} config the methods and properties of the 'base' class for the application.
6106  * 
6107  *  Generic Page handler - implement this to start your app..
6108  * 
6109  * eg.
6110  *  MyProject = new Roo.Document({
6111         events : {
6112             'load' : true // your events..
6113         },
6114         listeners : {
6115             'ready' : function() {
6116                 // fired on Roo.onReady()
6117             }
6118         }
6119  * 
6120  */
6121 Roo.Document = function(cfg) {
6122      
6123     this.addEvents({ 
6124         'ready' : true
6125     });
6126     Roo.util.Observable.call(this,cfg);
6127     
6128     var _this = this;
6129     
6130     Roo.onReady(function() {
6131         _this.fireEvent('ready');
6132     },null,false);
6133     
6134     
6135 }
6136
6137 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6138  * Based on:
6139  * Ext JS Library 1.1.1
6140  * Copyright(c) 2006-2007, Ext JS, LLC.
6141  *
6142  * Originally Released Under LGPL - original licence link has changed is not relivant.
6143  *
6144  * Fork - LGPL
6145  * <script type="text/javascript">
6146  */
6147
6148 /**
6149  * @class Roo.EventManager
6150  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6151  * several useful events directly.
6152  * See {@link Roo.EventObject} for more details on normalized event objects.
6153  * @singleton
6154  */
6155 Roo.EventManager = function(){
6156     var docReadyEvent, docReadyProcId, docReadyState = false;
6157     var resizeEvent, resizeTask, textEvent, textSize;
6158     var E = Roo.lib.Event;
6159     var D = Roo.lib.Dom;
6160
6161     
6162     
6163
6164     var fireDocReady = function(){
6165         if(!docReadyState){
6166             docReadyState = true;
6167             Roo.isReady = true;
6168             if(docReadyProcId){
6169                 clearInterval(docReadyProcId);
6170             }
6171             if(Roo.isGecko || Roo.isOpera) {
6172                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6173             }
6174             if(Roo.isIE){
6175                 var defer = document.getElementById("ie-deferred-loader");
6176                 if(defer){
6177                     defer.onreadystatechange = null;
6178                     defer.parentNode.removeChild(defer);
6179                 }
6180             }
6181             if(docReadyEvent){
6182                 docReadyEvent.fire();
6183                 docReadyEvent.clearListeners();
6184             }
6185         }
6186     };
6187     
6188     var initDocReady = function(){
6189         docReadyEvent = new Roo.util.Event();
6190         if(Roo.isGecko || Roo.isOpera) {
6191             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6192         }else if(Roo.isIE){
6193             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6194             var defer = document.getElementById("ie-deferred-loader");
6195             defer.onreadystatechange = function(){
6196                 if(this.readyState == "complete"){
6197                     fireDocReady();
6198                 }
6199             };
6200         }else if(Roo.isSafari){ 
6201             docReadyProcId = setInterval(function(){
6202                 var rs = document.readyState;
6203                 if(rs == "complete") {
6204                     fireDocReady();     
6205                  }
6206             }, 10);
6207         }
6208         // no matter what, make sure it fires on load
6209         E.on(window, "load", fireDocReady);
6210     };
6211
6212     var createBuffered = function(h, o){
6213         var task = new Roo.util.DelayedTask(h);
6214         return function(e){
6215             // create new event object impl so new events don't wipe out properties
6216             e = new Roo.EventObjectImpl(e);
6217             task.delay(o.buffer, h, null, [e]);
6218         };
6219     };
6220
6221     var createSingle = function(h, el, ename, fn){
6222         return function(e){
6223             Roo.EventManager.removeListener(el, ename, fn);
6224             h(e);
6225         };
6226     };
6227
6228     var createDelayed = function(h, o){
6229         return function(e){
6230             // create new event object impl so new events don't wipe out properties
6231             e = new Roo.EventObjectImpl(e);
6232             setTimeout(function(){
6233                 h(e);
6234             }, o.delay || 10);
6235         };
6236     };
6237     var transitionEndVal = false;
6238     
6239     var transitionEnd = function()
6240     {
6241         if (transitionEndVal) {
6242             return transitionEndVal;
6243         }
6244         var el = document.createElement('div');
6245
6246         var transEndEventNames = {
6247             WebkitTransition : 'webkitTransitionEnd',
6248             MozTransition    : 'transitionend',
6249             OTransition      : 'oTransitionEnd otransitionend',
6250             transition       : 'transitionend'
6251         };
6252     
6253         for (var name in transEndEventNames) {
6254             if (el.style[name] !== undefined) {
6255                 transitionEndVal = transEndEventNames[name];
6256                 return  transitionEndVal ;
6257             }
6258         }
6259     }
6260     
6261
6262     var listen = function(element, ename, opt, fn, scope){
6263         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6264         fn = fn || o.fn; scope = scope || o.scope;
6265         var el = Roo.getDom(element);
6266         
6267         
6268         if(!el){
6269             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6270         }
6271         
6272         if (ename == 'transitionend') {
6273             ename = transitionEnd();
6274         }
6275         var h = function(e){
6276             e = Roo.EventObject.setEvent(e);
6277             var t;
6278             if(o.delegate){
6279                 t = e.getTarget(o.delegate, el);
6280                 if(!t){
6281                     return;
6282                 }
6283             }else{
6284                 t = e.target;
6285             }
6286             if(o.stopEvent === true){
6287                 e.stopEvent();
6288             }
6289             if(o.preventDefault === true){
6290                e.preventDefault();
6291             }
6292             if(o.stopPropagation === true){
6293                 e.stopPropagation();
6294             }
6295
6296             if(o.normalized === false){
6297                 e = e.browserEvent;
6298             }
6299
6300             fn.call(scope || el, e, t, o);
6301         };
6302         if(o.delay){
6303             h = createDelayed(h, o);
6304         }
6305         if(o.single){
6306             h = createSingle(h, el, ename, fn);
6307         }
6308         if(o.buffer){
6309             h = createBuffered(h, o);
6310         }
6311         
6312         fn._handlers = fn._handlers || [];
6313         
6314         
6315         fn._handlers.push([Roo.id(el), ename, h]);
6316         
6317         
6318          
6319         E.on(el, ename, h);
6320         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6321             el.addEventListener("DOMMouseScroll", h, false);
6322             E.on(window, 'unload', function(){
6323                 el.removeEventListener("DOMMouseScroll", h, false);
6324             });
6325         }
6326         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6327             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6328         }
6329         return h;
6330     };
6331
6332     var stopListening = function(el, ename, fn){
6333         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6334         if(hds){
6335             for(var i = 0, len = hds.length; i < len; i++){
6336                 var h = hds[i];
6337                 if(h[0] == id && h[1] == ename){
6338                     hd = h[2];
6339                     hds.splice(i, 1);
6340                     break;
6341                 }
6342             }
6343         }
6344         E.un(el, ename, hd);
6345         el = Roo.getDom(el);
6346         if(ename == "mousewheel" && el.addEventListener){
6347             el.removeEventListener("DOMMouseScroll", hd, false);
6348         }
6349         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6350             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6351         }
6352     };
6353
6354     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6355     
6356     var pub = {
6357         
6358         
6359         /** 
6360          * Fix for doc tools
6361          * @scope Roo.EventManager
6362          */
6363         
6364         
6365         /** 
6366          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6367          * object with a Roo.EventObject
6368          * @param {Function} fn        The method the event invokes
6369          * @param {Object}   scope    An object that becomes the scope of the handler
6370          * @param {boolean}  override If true, the obj passed in becomes
6371          *                             the execution scope of the listener
6372          * @return {Function} The wrapped function
6373          * @deprecated
6374          */
6375         wrap : function(fn, scope, override){
6376             return function(e){
6377                 Roo.EventObject.setEvent(e);
6378                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6379             };
6380         },
6381         
6382         /**
6383      * Appends an event handler to an element (shorthand for addListener)
6384      * @param {String/HTMLElement}   element        The html element or id to assign the
6385      * @param {String}   eventName The type of event to listen for
6386      * @param {Function} handler The method the event invokes
6387      * @param {Object}   scope (optional) The scope in which to execute the handler
6388      * function. The handler function's "this" context.
6389      * @param {Object}   options (optional) An object containing handler configuration
6390      * properties. This may contain any of the following properties:<ul>
6391      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6392      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6393      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6394      * <li>preventDefault {Boolean} True to prevent the default action</li>
6395      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6396      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6397      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6398      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6399      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6400      * by the specified number of milliseconds. If the event fires again within that time, the original
6401      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6402      * </ul><br>
6403      * <p>
6404      * <b>Combining Options</b><br>
6405      * Using the options argument, it is possible to combine different types of listeners:<br>
6406      * <br>
6407      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6408      * Code:<pre><code>
6409 el.on('click', this.onClick, this, {
6410     single: true,
6411     delay: 100,
6412     stopEvent : true,
6413     forumId: 4
6414 });</code></pre>
6415      * <p>
6416      * <b>Attaching multiple handlers in 1 call</b><br>
6417       * The method also allows for a single argument to be passed which is a config object containing properties
6418      * which specify multiple handlers.
6419      * <p>
6420      * Code:<pre><code>
6421 el.on({
6422     'click' : {
6423         fn: this.onClick
6424         scope: this,
6425         delay: 100
6426     },
6427     'mouseover' : {
6428         fn: this.onMouseOver
6429         scope: this
6430     },
6431     'mouseout' : {
6432         fn: this.onMouseOut
6433         scope: this
6434     }
6435 });</code></pre>
6436      * <p>
6437      * Or a shorthand syntax:<br>
6438      * Code:<pre><code>
6439 el.on({
6440     'click' : this.onClick,
6441     'mouseover' : this.onMouseOver,
6442     'mouseout' : this.onMouseOut
6443     scope: this
6444 });</code></pre>
6445      */
6446         addListener : function(element, eventName, fn, scope, options){
6447             if(typeof eventName == "object"){
6448                 var o = eventName;
6449                 for(var e in o){
6450                     if(propRe.test(e)){
6451                         continue;
6452                     }
6453                     if(typeof o[e] == "function"){
6454                         // shared options
6455                         listen(element, e, o, o[e], o.scope);
6456                     }else{
6457                         // individual options
6458                         listen(element, e, o[e]);
6459                     }
6460                 }
6461                 return;
6462             }
6463             return listen(element, eventName, options, fn, scope);
6464         },
6465         
6466         /**
6467          * Removes an event handler
6468          *
6469          * @param {String/HTMLElement}   element        The id or html element to remove the 
6470          *                             event from
6471          * @param {String}   eventName     The type of event
6472          * @param {Function} fn
6473          * @return {Boolean} True if a listener was actually removed
6474          */
6475         removeListener : function(element, eventName, fn){
6476             return stopListening(element, eventName, fn);
6477         },
6478         
6479         /**
6480          * Fires when the document is ready (before onload and before images are loaded). Can be 
6481          * accessed shorthanded Roo.onReady().
6482          * @param {Function} fn        The method the event invokes
6483          * @param {Object}   scope    An  object that becomes the scope of the handler
6484          * @param {boolean}  options
6485          */
6486         onDocumentReady : function(fn, scope, options){
6487             if(docReadyState){ // if it already fired
6488                 docReadyEvent.addListener(fn, scope, options);
6489                 docReadyEvent.fire();
6490                 docReadyEvent.clearListeners();
6491                 return;
6492             }
6493             if(!docReadyEvent){
6494                 initDocReady();
6495             }
6496             docReadyEvent.addListener(fn, scope, options);
6497         },
6498         
6499         /**
6500          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6501          * @param {Function} fn        The method the event invokes
6502          * @param {Object}   scope    An object that becomes the scope of the handler
6503          * @param {boolean}  options
6504          */
6505         onWindowResize : function(fn, scope, options){
6506             if(!resizeEvent){
6507                 resizeEvent = new Roo.util.Event();
6508                 resizeTask = new Roo.util.DelayedTask(function(){
6509                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6510                 });
6511                 E.on(window, "resize", function(){
6512                     if(Roo.isIE){
6513                         resizeTask.delay(50);
6514                     }else{
6515                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6516                     }
6517                 });
6518             }
6519             resizeEvent.addListener(fn, scope, options);
6520         },
6521
6522         /**
6523          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6524          * @param {Function} fn        The method the event invokes
6525          * @param {Object}   scope    An object that becomes the scope of the handler
6526          * @param {boolean}  options
6527          */
6528         onTextResize : function(fn, scope, options){
6529             if(!textEvent){
6530                 textEvent = new Roo.util.Event();
6531                 var textEl = new Roo.Element(document.createElement('div'));
6532                 textEl.dom.className = 'x-text-resize';
6533                 textEl.dom.innerHTML = 'X';
6534                 textEl.appendTo(document.body);
6535                 textSize = textEl.dom.offsetHeight;
6536                 setInterval(function(){
6537                     if(textEl.dom.offsetHeight != textSize){
6538                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6539                     }
6540                 }, this.textResizeInterval);
6541             }
6542             textEvent.addListener(fn, scope, options);
6543         },
6544
6545         /**
6546          * Removes the passed window resize listener.
6547          * @param {Function} fn        The method the event invokes
6548          * @param {Object}   scope    The scope of handler
6549          */
6550         removeResizeListener : function(fn, scope){
6551             if(resizeEvent){
6552                 resizeEvent.removeListener(fn, scope);
6553             }
6554         },
6555
6556         // private
6557         fireResize : function(){
6558             if(resizeEvent){
6559                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6560             }   
6561         },
6562         /**
6563          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6564          */
6565         ieDeferSrc : false,
6566         /**
6567          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6568          */
6569         textResizeInterval : 50
6570     };
6571     
6572     /**
6573      * Fix for doc tools
6574      * @scopeAlias pub=Roo.EventManager
6575      */
6576     
6577      /**
6578      * Appends an event handler to an element (shorthand for addListener)
6579      * @param {String/HTMLElement}   element        The html element or id to assign the
6580      * @param {String}   eventName The type of event to listen for
6581      * @param {Function} handler The method the event invokes
6582      * @param {Object}   scope (optional) The scope in which to execute the handler
6583      * function. The handler function's "this" context.
6584      * @param {Object}   options (optional) An object containing handler configuration
6585      * properties. This may contain any of the following properties:<ul>
6586      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6587      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6588      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6589      * <li>preventDefault {Boolean} True to prevent the default action</li>
6590      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6591      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6592      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6593      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6594      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6595      * by the specified number of milliseconds. If the event fires again within that time, the original
6596      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6597      * </ul><br>
6598      * <p>
6599      * <b>Combining Options</b><br>
6600      * Using the options argument, it is possible to combine different types of listeners:<br>
6601      * <br>
6602      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6603      * Code:<pre><code>
6604 el.on('click', this.onClick, this, {
6605     single: true,
6606     delay: 100,
6607     stopEvent : true,
6608     forumId: 4
6609 });</code></pre>
6610      * <p>
6611      * <b>Attaching multiple handlers in 1 call</b><br>
6612       * The method also allows for a single argument to be passed which is a config object containing properties
6613      * which specify multiple handlers.
6614      * <p>
6615      * Code:<pre><code>
6616 el.on({
6617     'click' : {
6618         fn: this.onClick
6619         scope: this,
6620         delay: 100
6621     },
6622     'mouseover' : {
6623         fn: this.onMouseOver
6624         scope: this
6625     },
6626     'mouseout' : {
6627         fn: this.onMouseOut
6628         scope: this
6629     }
6630 });</code></pre>
6631      * <p>
6632      * Or a shorthand syntax:<br>
6633      * Code:<pre><code>
6634 el.on({
6635     'click' : this.onClick,
6636     'mouseover' : this.onMouseOver,
6637     'mouseout' : this.onMouseOut
6638     scope: this
6639 });</code></pre>
6640      */
6641     pub.on = pub.addListener;
6642     pub.un = pub.removeListener;
6643
6644     pub.stoppedMouseDownEvent = new Roo.util.Event();
6645     return pub;
6646 }();
6647 /**
6648   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6649   * @param {Function} fn        The method the event invokes
6650   * @param {Object}   scope    An  object that becomes the scope of the handler
6651   * @param {boolean}  override If true, the obj passed in becomes
6652   *                             the execution scope of the listener
6653   * @member Roo
6654   * @method onReady
6655  */
6656 Roo.onReady = Roo.EventManager.onDocumentReady;
6657
6658 Roo.onReady(function(){
6659     var bd = Roo.get(document.body);
6660     if(!bd){ return; }
6661
6662     var cls = [
6663             Roo.isIE ? "roo-ie"
6664             : Roo.isIE11 ? "roo-ie11"
6665             : Roo.isEdge ? "roo-edge"
6666             : Roo.isGecko ? "roo-gecko"
6667             : Roo.isOpera ? "roo-opera"
6668             : Roo.isSafari ? "roo-safari" : ""];
6669
6670     if(Roo.isMac){
6671         cls.push("roo-mac");
6672     }
6673     if(Roo.isLinux){
6674         cls.push("roo-linux");
6675     }
6676     if(Roo.isIOS){
6677         cls.push("roo-ios");
6678     }
6679     if(Roo.isTouch){
6680         cls.push("roo-touch");
6681     }
6682     if(Roo.isBorderBox){
6683         cls.push('roo-border-box');
6684     }
6685     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6686         var p = bd.dom.parentNode;
6687         if(p){
6688             p.className += ' roo-strict';
6689         }
6690     }
6691     bd.addClass(cls.join(' '));
6692 });
6693
6694 /**
6695  * @class Roo.EventObject
6696  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6697  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6698  * Example:
6699  * <pre><code>
6700  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6701     e.preventDefault();
6702     var target = e.getTarget();
6703     ...
6704  }
6705  var myDiv = Roo.get("myDiv");
6706  myDiv.on("click", handleClick);
6707  //or
6708  Roo.EventManager.on("myDiv", 'click', handleClick);
6709  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6710  </code></pre>
6711  * @singleton
6712  */
6713 Roo.EventObject = function(){
6714     
6715     var E = Roo.lib.Event;
6716     
6717     // safari keypress events for special keys return bad keycodes
6718     var safariKeys = {
6719         63234 : 37, // left
6720         63235 : 39, // right
6721         63232 : 38, // up
6722         63233 : 40, // down
6723         63276 : 33, // page up
6724         63277 : 34, // page down
6725         63272 : 46, // delete
6726         63273 : 36, // home
6727         63275 : 35  // end
6728     };
6729
6730     // normalize button clicks
6731     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6732                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6733
6734     Roo.EventObjectImpl = function(e){
6735         if(e){
6736             this.setEvent(e.browserEvent || e);
6737         }
6738     };
6739     Roo.EventObjectImpl.prototype = {
6740         /**
6741          * Used to fix doc tools.
6742          * @scope Roo.EventObject.prototype
6743          */
6744             
6745
6746         
6747         
6748         /** The normal browser event */
6749         browserEvent : null,
6750         /** The button pressed in a mouse event */
6751         button : -1,
6752         /** True if the shift key was down during the event */
6753         shiftKey : false,
6754         /** True if the control key was down during the event */
6755         ctrlKey : false,
6756         /** True if the alt key was down during the event */
6757         altKey : false,
6758
6759         /** Key constant 
6760         * @type Number */
6761         BACKSPACE : 8,
6762         /** Key constant 
6763         * @type Number */
6764         TAB : 9,
6765         /** Key constant 
6766         * @type Number */
6767         RETURN : 13,
6768         /** Key constant 
6769         * @type Number */
6770         ENTER : 13,
6771         /** Key constant 
6772         * @type Number */
6773         SHIFT : 16,
6774         /** Key constant 
6775         * @type Number */
6776         CONTROL : 17,
6777         /** Key constant 
6778         * @type Number */
6779         ESC : 27,
6780         /** Key constant 
6781         * @type Number */
6782         SPACE : 32,
6783         /** Key constant 
6784         * @type Number */
6785         PAGEUP : 33,
6786         /** Key constant 
6787         * @type Number */
6788         PAGEDOWN : 34,
6789         /** Key constant 
6790         * @type Number */
6791         END : 35,
6792         /** Key constant 
6793         * @type Number */
6794         HOME : 36,
6795         /** Key constant 
6796         * @type Number */
6797         LEFT : 37,
6798         /** Key constant 
6799         * @type Number */
6800         UP : 38,
6801         /** Key constant 
6802         * @type Number */
6803         RIGHT : 39,
6804         /** Key constant 
6805         * @type Number */
6806         DOWN : 40,
6807         /** Key constant 
6808         * @type Number */
6809         DELETE : 46,
6810         /** Key constant 
6811         * @type Number */
6812         F5 : 116,
6813
6814            /** @private */
6815         setEvent : function(e){
6816             if(e == this || (e && e.browserEvent)){ // already wrapped
6817                 return e;
6818             }
6819             this.browserEvent = e;
6820             if(e){
6821                 // normalize buttons
6822                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6823                 if(e.type == 'click' && this.button == -1){
6824                     this.button = 0;
6825                 }
6826                 this.type = e.type;
6827                 this.shiftKey = e.shiftKey;
6828                 // mac metaKey behaves like ctrlKey
6829                 this.ctrlKey = e.ctrlKey || e.metaKey;
6830                 this.altKey = e.altKey;
6831                 // in getKey these will be normalized for the mac
6832                 this.keyCode = e.keyCode;
6833                 // keyup warnings on firefox.
6834                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6835                 // cache the target for the delayed and or buffered events
6836                 this.target = E.getTarget(e);
6837                 // same for XY
6838                 this.xy = E.getXY(e);
6839             }else{
6840                 this.button = -1;
6841                 this.shiftKey = false;
6842                 this.ctrlKey = false;
6843                 this.altKey = false;
6844                 this.keyCode = 0;
6845                 this.charCode =0;
6846                 this.target = null;
6847                 this.xy = [0, 0];
6848             }
6849             return this;
6850         },
6851
6852         /**
6853          * Stop the event (preventDefault and stopPropagation)
6854          */
6855         stopEvent : function(){
6856             if(this.browserEvent){
6857                 if(this.browserEvent.type == 'mousedown'){
6858                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6859                 }
6860                 E.stopEvent(this.browserEvent);
6861             }
6862         },
6863
6864         /**
6865          * Prevents the browsers default handling of the event.
6866          */
6867         preventDefault : function(){
6868             if(this.browserEvent){
6869                 E.preventDefault(this.browserEvent);
6870             }
6871         },
6872
6873         /** @private */
6874         isNavKeyPress : function(){
6875             var k = this.keyCode;
6876             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6877             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6878         },
6879
6880         isSpecialKey : function(){
6881             var k = this.keyCode;
6882             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6883             (k == 16) || (k == 17) ||
6884             (k >= 18 && k <= 20) ||
6885             (k >= 33 && k <= 35) ||
6886             (k >= 36 && k <= 39) ||
6887             (k >= 44 && k <= 45);
6888         },
6889         /**
6890          * Cancels bubbling of the event.
6891          */
6892         stopPropagation : function(){
6893             if(this.browserEvent){
6894                 if(this.type == 'mousedown'){
6895                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6896                 }
6897                 E.stopPropagation(this.browserEvent);
6898             }
6899         },
6900
6901         /**
6902          * Gets the key code for the event.
6903          * @return {Number}
6904          */
6905         getCharCode : function(){
6906             return this.charCode || this.keyCode;
6907         },
6908
6909         /**
6910          * Returns a normalized keyCode for the event.
6911          * @return {Number} The key code
6912          */
6913         getKey : function(){
6914             var k = this.keyCode || this.charCode;
6915             return Roo.isSafari ? (safariKeys[k] || k) : k;
6916         },
6917
6918         /**
6919          * Gets the x coordinate of the event.
6920          * @return {Number}
6921          */
6922         getPageX : function(){
6923             return this.xy[0];
6924         },
6925
6926         /**
6927          * Gets the y coordinate of the event.
6928          * @return {Number}
6929          */
6930         getPageY : function(){
6931             return this.xy[1];
6932         },
6933
6934         /**
6935          * Gets the time of the event.
6936          * @return {Number}
6937          */
6938         getTime : function(){
6939             if(this.browserEvent){
6940                 return E.getTime(this.browserEvent);
6941             }
6942             return null;
6943         },
6944
6945         /**
6946          * Gets the page coordinates of the event.
6947          * @return {Array} The xy values like [x, y]
6948          */
6949         getXY : function(){
6950             return this.xy;
6951         },
6952
6953         /**
6954          * Gets the target for the event.
6955          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6956          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6957                 search as a number or element (defaults to 10 || document.body)
6958          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6959          * @return {HTMLelement}
6960          */
6961         getTarget : function(selector, maxDepth, returnEl){
6962             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6963         },
6964         /**
6965          * Gets the related target.
6966          * @return {HTMLElement}
6967          */
6968         getRelatedTarget : function(){
6969             if(this.browserEvent){
6970                 return E.getRelatedTarget(this.browserEvent);
6971             }
6972             return null;
6973         },
6974
6975         /**
6976          * Normalizes mouse wheel delta across browsers
6977          * @return {Number} The delta
6978          */
6979         getWheelDelta : function(){
6980             var e = this.browserEvent;
6981             var delta = 0;
6982             if(e.wheelDelta){ /* IE/Opera. */
6983                 delta = e.wheelDelta/120;
6984             }else if(e.detail){ /* Mozilla case. */
6985                 delta = -e.detail/3;
6986             }
6987             return delta;
6988         },
6989
6990         /**
6991          * Returns true if the control, meta, shift or alt key was pressed during this event.
6992          * @return {Boolean}
6993          */
6994         hasModifier : function(){
6995             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6996         },
6997
6998         /**
6999          * Returns true if the target of this event equals el or is a child of el
7000          * @param {String/HTMLElement/Element} el
7001          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7002          * @return {Boolean}
7003          */
7004         within : function(el, related){
7005             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7006             return t && Roo.fly(el).contains(t);
7007         },
7008
7009         getPoint : function(){
7010             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7011         }
7012     };
7013
7014     return new Roo.EventObjectImpl();
7015 }();
7016             
7017     /*
7018  * Based on:
7019  * Ext JS Library 1.1.1
7020  * Copyright(c) 2006-2007, Ext JS, LLC.
7021  *
7022  * Originally Released Under LGPL - original licence link has changed is not relivant.
7023  *
7024  * Fork - LGPL
7025  * <script type="text/javascript">
7026  */
7027
7028  
7029 // was in Composite Element!??!?!
7030  
7031 (function(){
7032     var D = Roo.lib.Dom;
7033     var E = Roo.lib.Event;
7034     var A = Roo.lib.Anim;
7035
7036     // local style camelizing for speed
7037     var propCache = {};
7038     var camelRe = /(-[a-z])/gi;
7039     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7040     var view = document.defaultView;
7041
7042 /**
7043  * @class Roo.Element
7044  * Represents an Element in the DOM.<br><br>
7045  * Usage:<br>
7046 <pre><code>
7047 var el = Roo.get("my-div");
7048
7049 // or with getEl
7050 var el = getEl("my-div");
7051
7052 // or with a DOM element
7053 var el = Roo.get(myDivElement);
7054 </code></pre>
7055  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7056  * each call instead of constructing a new one.<br><br>
7057  * <b>Animations</b><br />
7058  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7059  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7060 <pre>
7061 Option    Default   Description
7062 --------- --------  ---------------------------------------------
7063 duration  .35       The duration of the animation in seconds
7064 easing    easeOut   The YUI easing method
7065 callback  none      A function to execute when the anim completes
7066 scope     this      The scope (this) of the callback function
7067 </pre>
7068 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7069 * manipulate the animation. Here's an example:
7070 <pre><code>
7071 var el = Roo.get("my-div");
7072
7073 // no animation
7074 el.setWidth(100);
7075
7076 // default animation
7077 el.setWidth(100, true);
7078
7079 // animation with some options set
7080 el.setWidth(100, {
7081     duration: 1,
7082     callback: this.foo,
7083     scope: this
7084 });
7085
7086 // using the "anim" property to get the Anim object
7087 var opt = {
7088     duration: 1,
7089     callback: this.foo,
7090     scope: this
7091 };
7092 el.setWidth(100, opt);
7093 ...
7094 if(opt.anim.isAnimated()){
7095     opt.anim.stop();
7096 }
7097 </code></pre>
7098 * <b> Composite (Collections of) Elements</b><br />
7099  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7100  * @constructor Create a new Element directly.
7101  * @param {String/HTMLElement} element
7102  * @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).
7103  */
7104     Roo.Element = function(element, forceNew){
7105         var dom = typeof element == "string" ?
7106                 document.getElementById(element) : element;
7107         if(!dom){ // invalid id/element
7108             return null;
7109         }
7110         var id = dom.id;
7111         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7112             return Roo.Element.cache[id];
7113         }
7114
7115         /**
7116          * The DOM element
7117          * @type HTMLElement
7118          */
7119         this.dom = dom;
7120
7121         /**
7122          * The DOM element ID
7123          * @type String
7124          */
7125         this.id = id || Roo.id(dom);
7126     };
7127
7128     var El = Roo.Element;
7129
7130     El.prototype = {
7131         /**
7132          * The element's default display mode  (defaults to "")
7133          * @type String
7134          */
7135         originalDisplay : "",
7136
7137         visibilityMode : 1,
7138         /**
7139          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7140          * @type String
7141          */
7142         defaultUnit : "px",
7143         
7144         /**
7145          * Sets the element's visibility mode. When setVisible() is called it
7146          * will use this to determine whether to set the visibility or the display property.
7147          * @param visMode Element.VISIBILITY or Element.DISPLAY
7148          * @return {Roo.Element} this
7149          */
7150         setVisibilityMode : function(visMode){
7151             this.visibilityMode = visMode;
7152             return this;
7153         },
7154         /**
7155          * Convenience method for setVisibilityMode(Element.DISPLAY)
7156          * @param {String} display (optional) What to set display to when visible
7157          * @return {Roo.Element} this
7158          */
7159         enableDisplayMode : function(display){
7160             this.setVisibilityMode(El.DISPLAY);
7161             if(typeof display != "undefined") { this.originalDisplay = display; }
7162             return this;
7163         },
7164
7165         /**
7166          * 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)
7167          * @param {String} selector The simple selector to test
7168          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7169                 search as a number or element (defaults to 10 || document.body)
7170          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7171          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7172          */
7173         findParent : function(simpleSelector, maxDepth, returnEl){
7174             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7175             maxDepth = maxDepth || 50;
7176             if(typeof maxDepth != "number"){
7177                 stopEl = Roo.getDom(maxDepth);
7178                 maxDepth = 10;
7179             }
7180             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7181                 if(dq.is(p, simpleSelector)){
7182                     return returnEl ? Roo.get(p) : p;
7183                 }
7184                 depth++;
7185                 p = p.parentNode;
7186             }
7187             return null;
7188         },
7189
7190
7191         /**
7192          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7193          * @param {String} selector The simple selector to test
7194          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7195                 search as a number or element (defaults to 10 || document.body)
7196          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7197          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7198          */
7199         findParentNode : function(simpleSelector, maxDepth, returnEl){
7200             var p = Roo.fly(this.dom.parentNode, '_internal');
7201             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7202         },
7203         
7204         /**
7205          * Looks at  the scrollable parent element
7206          */
7207         findScrollableParent : function()
7208         {
7209             var overflowRegex = /(auto|scroll)/;
7210             
7211             if(this.getStyle('position') === 'fixed'){
7212                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7213             }
7214             
7215             var excludeStaticParent = this.getStyle('position') === "absolute";
7216             
7217             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7218                 
7219                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7220                     continue;
7221                 }
7222                 
7223                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7224                     return parent;
7225                 }
7226                 
7227                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7228                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7229                 }
7230             }
7231             
7232             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7233         },
7234
7235         /**
7236          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7237          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7238          * @param {String} selector The simple selector to test
7239          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7240                 search as a number or element (defaults to 10 || document.body)
7241          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7242          */
7243         up : function(simpleSelector, maxDepth){
7244             return this.findParentNode(simpleSelector, maxDepth, true);
7245         },
7246
7247
7248
7249         /**
7250          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7251          * @param {String} selector The simple selector to test
7252          * @return {Boolean} True if this element matches the selector, else false
7253          */
7254         is : function(simpleSelector){
7255             return Roo.DomQuery.is(this.dom, simpleSelector);
7256         },
7257
7258         /**
7259          * Perform animation on this element.
7260          * @param {Object} args The YUI animation control args
7261          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7262          * @param {Function} onComplete (optional) Function to call when animation completes
7263          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7264          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7265          * @return {Roo.Element} this
7266          */
7267         animate : function(args, duration, onComplete, easing, animType){
7268             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7269             return this;
7270         },
7271
7272         /*
7273          * @private Internal animation call
7274          */
7275         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7276             animType = animType || 'run';
7277             opt = opt || {};
7278             var anim = Roo.lib.Anim[animType](
7279                 this.dom, args,
7280                 (opt.duration || defaultDur) || .35,
7281                 (opt.easing || defaultEase) || 'easeOut',
7282                 function(){
7283                     Roo.callback(cb, this);
7284                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7285                 },
7286                 this
7287             );
7288             opt.anim = anim;
7289             return anim;
7290         },
7291
7292         // private legacy anim prep
7293         preanim : function(a, i){
7294             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7295         },
7296
7297         /**
7298          * Removes worthless text nodes
7299          * @param {Boolean} forceReclean (optional) By default the element
7300          * keeps track if it has been cleaned already so
7301          * you can call this over and over. However, if you update the element and
7302          * need to force a reclean, you can pass true.
7303          */
7304         clean : function(forceReclean){
7305             if(this.isCleaned && forceReclean !== true){
7306                 return this;
7307             }
7308             var ns = /\S/;
7309             var d = this.dom, n = d.firstChild, ni = -1;
7310             while(n){
7311                 var nx = n.nextSibling;
7312                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7313                     d.removeChild(n);
7314                 }else{
7315                     n.nodeIndex = ++ni;
7316                 }
7317                 n = nx;
7318             }
7319             this.isCleaned = true;
7320             return this;
7321         },
7322
7323         // private
7324         calcOffsetsTo : function(el){
7325             el = Roo.get(el);
7326             var d = el.dom;
7327             var restorePos = false;
7328             if(el.getStyle('position') == 'static'){
7329                 el.position('relative');
7330                 restorePos = true;
7331             }
7332             var x = 0, y =0;
7333             var op = this.dom;
7334             while(op && op != d && op.tagName != 'HTML'){
7335                 x+= op.offsetLeft;
7336                 y+= op.offsetTop;
7337                 op = op.offsetParent;
7338             }
7339             if(restorePos){
7340                 el.position('static');
7341             }
7342             return [x, y];
7343         },
7344
7345         /**
7346          * Scrolls this element into view within the passed container.
7347          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7348          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7349          * @return {Roo.Element} this
7350          */
7351         scrollIntoView : function(container, hscroll){
7352             var c = Roo.getDom(container) || document.body;
7353             var el = this.dom;
7354
7355             var o = this.calcOffsetsTo(c),
7356                 l = o[0],
7357                 t = o[1],
7358                 b = t+el.offsetHeight,
7359                 r = l+el.offsetWidth;
7360
7361             var ch = c.clientHeight;
7362             var ct = parseInt(c.scrollTop, 10);
7363             var cl = parseInt(c.scrollLeft, 10);
7364             var cb = ct + ch;
7365             var cr = cl + c.clientWidth;
7366
7367             if(t < ct){
7368                 c.scrollTop = t;
7369             }else if(b > cb){
7370                 c.scrollTop = b-ch;
7371             }
7372
7373             if(hscroll !== false){
7374                 if(l < cl){
7375                     c.scrollLeft = l;
7376                 }else if(r > cr){
7377                     c.scrollLeft = r-c.clientWidth;
7378                 }
7379             }
7380             return this;
7381         },
7382
7383         // private
7384         scrollChildIntoView : function(child, hscroll){
7385             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7386         },
7387
7388         /**
7389          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7390          * the new height may not be available immediately.
7391          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7392          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7393          * @param {Function} onComplete (optional) Function to call when animation completes
7394          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7395          * @return {Roo.Element} this
7396          */
7397         autoHeight : function(animate, duration, onComplete, easing){
7398             var oldHeight = this.getHeight();
7399             this.clip();
7400             this.setHeight(1); // force clipping
7401             setTimeout(function(){
7402                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7403                 if(!animate){
7404                     this.setHeight(height);
7405                     this.unclip();
7406                     if(typeof onComplete == "function"){
7407                         onComplete();
7408                     }
7409                 }else{
7410                     this.setHeight(oldHeight); // restore original height
7411                     this.setHeight(height, animate, duration, function(){
7412                         this.unclip();
7413                         if(typeof onComplete == "function") { onComplete(); }
7414                     }.createDelegate(this), easing);
7415                 }
7416             }.createDelegate(this), 0);
7417             return this;
7418         },
7419
7420         /**
7421          * Returns true if this element is an ancestor of the passed element
7422          * @param {HTMLElement/String} el The element to check
7423          * @return {Boolean} True if this element is an ancestor of el, else false
7424          */
7425         contains : function(el){
7426             if(!el){return false;}
7427             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7428         },
7429
7430         /**
7431          * Checks whether the element is currently visible using both visibility and display properties.
7432          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7433          * @return {Boolean} True if the element is currently visible, else false
7434          */
7435         isVisible : function(deep) {
7436             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7437             if(deep !== true || !vis){
7438                 return vis;
7439             }
7440             var p = this.dom.parentNode;
7441             while(p && p.tagName.toLowerCase() != "body"){
7442                 if(!Roo.fly(p, '_isVisible').isVisible()){
7443                     return false;
7444                 }
7445                 p = p.parentNode;
7446             }
7447             return true;
7448         },
7449
7450         /**
7451          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7452          * @param {String} selector The CSS selector
7453          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7454          * @return {CompositeElement/CompositeElementLite} The composite element
7455          */
7456         select : function(selector, unique){
7457             return El.select(selector, unique, this.dom);
7458         },
7459
7460         /**
7461          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7462          * @param {String} selector The CSS selector
7463          * @return {Array} An array of the matched nodes
7464          */
7465         query : function(selector, unique){
7466             return Roo.DomQuery.select(selector, this.dom);
7467         },
7468
7469         /**
7470          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7471          * @param {String} selector The CSS selector
7472          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7473          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7474          */
7475         child : function(selector, returnDom){
7476             var n = Roo.DomQuery.selectNode(selector, this.dom);
7477             return returnDom ? n : Roo.get(n);
7478         },
7479
7480         /**
7481          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7482          * @param {String} selector The CSS selector
7483          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7484          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7485          */
7486         down : function(selector, returnDom){
7487             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7488             return returnDom ? n : Roo.get(n);
7489         },
7490
7491         /**
7492          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7493          * @param {String} group The group the DD object is member of
7494          * @param {Object} config The DD config object
7495          * @param {Object} overrides An object containing methods to override/implement on the DD object
7496          * @return {Roo.dd.DD} The DD object
7497          */
7498         initDD : function(group, config, overrides){
7499             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7500             return Roo.apply(dd, overrides);
7501         },
7502
7503         /**
7504          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7505          * @param {String} group The group the DDProxy object is member of
7506          * @param {Object} config The DDProxy config object
7507          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7508          * @return {Roo.dd.DDProxy} The DDProxy object
7509          */
7510         initDDProxy : function(group, config, overrides){
7511             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7512             return Roo.apply(dd, overrides);
7513         },
7514
7515         /**
7516          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7517          * @param {String} group The group the DDTarget object is member of
7518          * @param {Object} config The DDTarget config object
7519          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7520          * @return {Roo.dd.DDTarget} The DDTarget object
7521          */
7522         initDDTarget : function(group, config, overrides){
7523             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7524             return Roo.apply(dd, overrides);
7525         },
7526
7527         /**
7528          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7529          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7530          * @param {Boolean} visible Whether the element is visible
7531          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7532          * @return {Roo.Element} this
7533          */
7534          setVisible : function(visible, animate){
7535             if(!animate || !A){
7536                 if(this.visibilityMode == El.DISPLAY){
7537                     this.setDisplayed(visible);
7538                 }else{
7539                     this.fixDisplay();
7540                     this.dom.style.visibility = visible ? "visible" : "hidden";
7541                 }
7542             }else{
7543                 // closure for composites
7544                 var dom = this.dom;
7545                 var visMode = this.visibilityMode;
7546                 if(visible){
7547                     this.setOpacity(.01);
7548                     this.setVisible(true);
7549                 }
7550                 this.anim({opacity: { to: (visible?1:0) }},
7551                       this.preanim(arguments, 1),
7552                       null, .35, 'easeIn', function(){
7553                          if(!visible){
7554                              if(visMode == El.DISPLAY){
7555                                  dom.style.display = "none";
7556                              }else{
7557                                  dom.style.visibility = "hidden";
7558                              }
7559                              Roo.get(dom).setOpacity(1);
7560                          }
7561                      });
7562             }
7563             return this;
7564         },
7565
7566         /**
7567          * Returns true if display is not "none"
7568          * @return {Boolean}
7569          */
7570         isDisplayed : function() {
7571             return this.getStyle("display") != "none";
7572         },
7573
7574         /**
7575          * Toggles the element's visibility or display, depending on visibility mode.
7576          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7577          * @return {Roo.Element} this
7578          */
7579         toggle : function(animate){
7580             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7581             return this;
7582         },
7583
7584         /**
7585          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7586          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7587          * @return {Roo.Element} this
7588          */
7589         setDisplayed : function(value) {
7590             if(typeof value == "boolean"){
7591                value = value ? this.originalDisplay : "none";
7592             }
7593             this.setStyle("display", value);
7594             return this;
7595         },
7596
7597         /**
7598          * Tries to focus the element. Any exceptions are caught and ignored.
7599          * @return {Roo.Element} this
7600          */
7601         focus : function() {
7602             try{
7603                 this.dom.focus();
7604             }catch(e){}
7605             return this;
7606         },
7607
7608         /**
7609          * Tries to blur the element. Any exceptions are caught and ignored.
7610          * @return {Roo.Element} this
7611          */
7612         blur : function() {
7613             try{
7614                 this.dom.blur();
7615             }catch(e){}
7616             return this;
7617         },
7618
7619         /**
7620          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7621          * @param {String/Array} className The CSS class to add, or an array of classes
7622          * @return {Roo.Element} this
7623          */
7624         addClass : function(className){
7625             if(className instanceof Array){
7626                 for(var i = 0, len = className.length; i < len; i++) {
7627                     this.addClass(className[i]);
7628                 }
7629             }else{
7630                 if(className && !this.hasClass(className)){
7631                     this.dom.className = this.dom.className + " " + className;
7632                 }
7633             }
7634             return this;
7635         },
7636
7637         /**
7638          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7639          * @param {String/Array} className The CSS class to add, or an array of classes
7640          * @return {Roo.Element} this
7641          */
7642         radioClass : function(className){
7643             var siblings = this.dom.parentNode.childNodes;
7644             for(var i = 0; i < siblings.length; i++) {
7645                 var s = siblings[i];
7646                 if(s.nodeType == 1){
7647                     Roo.get(s).removeClass(className);
7648                 }
7649             }
7650             this.addClass(className);
7651             return this;
7652         },
7653
7654         /**
7655          * Removes one or more CSS classes from the element.
7656          * @param {String/Array} className The CSS class to remove, or an array of classes
7657          * @return {Roo.Element} this
7658          */
7659         removeClass : function(className){
7660             if(!className || !this.dom.className){
7661                 return this;
7662             }
7663             if(className instanceof Array){
7664                 for(var i = 0, len = className.length; i < len; i++) {
7665                     this.removeClass(className[i]);
7666                 }
7667             }else{
7668                 if(this.hasClass(className)){
7669                     var re = this.classReCache[className];
7670                     if (!re) {
7671                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7672                        this.classReCache[className] = re;
7673                     }
7674                     this.dom.className =
7675                         this.dom.className.replace(re, " ");
7676                 }
7677             }
7678             return this;
7679         },
7680
7681         // private
7682         classReCache: {},
7683
7684         /**
7685          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7686          * @param {String} className The CSS class to toggle
7687          * @return {Roo.Element} this
7688          */
7689         toggleClass : function(className){
7690             if(this.hasClass(className)){
7691                 this.removeClass(className);
7692             }else{
7693                 this.addClass(className);
7694             }
7695             return this;
7696         },
7697
7698         /**
7699          * Checks if the specified CSS class exists on this element's DOM node.
7700          * @param {String} className The CSS class to check for
7701          * @return {Boolean} True if the class exists, else false
7702          */
7703         hasClass : function(className){
7704             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7705         },
7706
7707         /**
7708          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7709          * @param {String} oldClassName The CSS class to replace
7710          * @param {String} newClassName The replacement CSS class
7711          * @return {Roo.Element} this
7712          */
7713         replaceClass : function(oldClassName, newClassName){
7714             this.removeClass(oldClassName);
7715             this.addClass(newClassName);
7716             return this;
7717         },
7718
7719         /**
7720          * Returns an object with properties matching the styles requested.
7721          * For example, el.getStyles('color', 'font-size', 'width') might return
7722          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7723          * @param {String} style1 A style name
7724          * @param {String} style2 A style name
7725          * @param {String} etc.
7726          * @return {Object} The style object
7727          */
7728         getStyles : function(){
7729             var a = arguments, len = a.length, r = {};
7730             for(var i = 0; i < len; i++){
7731                 r[a[i]] = this.getStyle(a[i]);
7732             }
7733             return r;
7734         },
7735
7736         /**
7737          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7738          * @param {String} property The style property whose value is returned.
7739          * @return {String} The current value of the style property for this element.
7740          */
7741         getStyle : function(){
7742             return view && view.getComputedStyle ?
7743                 function(prop){
7744                     var el = this.dom, v, cs, camel;
7745                     if(prop == 'float'){
7746                         prop = "cssFloat";
7747                     }
7748                     if(el.style && (v = el.style[prop])){
7749                         return v;
7750                     }
7751                     if(cs = view.getComputedStyle(el, "")){
7752                         if(!(camel = propCache[prop])){
7753                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7754                         }
7755                         return cs[camel];
7756                     }
7757                     return null;
7758                 } :
7759                 function(prop){
7760                     var el = this.dom, v, cs, camel;
7761                     if(prop == 'opacity'){
7762                         if(typeof el.style.filter == 'string'){
7763                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7764                             if(m){
7765                                 var fv = parseFloat(m[1]);
7766                                 if(!isNaN(fv)){
7767                                     return fv ? fv / 100 : 0;
7768                                 }
7769                             }
7770                         }
7771                         return 1;
7772                     }else if(prop == 'float'){
7773                         prop = "styleFloat";
7774                     }
7775                     if(!(camel = propCache[prop])){
7776                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7777                     }
7778                     if(v = el.style[camel]){
7779                         return v;
7780                     }
7781                     if(cs = el.currentStyle){
7782                         return cs[camel];
7783                     }
7784                     return null;
7785                 };
7786         }(),
7787
7788         /**
7789          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7790          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7791          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7792          * @return {Roo.Element} this
7793          */
7794         setStyle : function(prop, value){
7795             if(typeof prop == "string"){
7796                 
7797                 if (prop == 'float') {
7798                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7799                     return this;
7800                 }
7801                 
7802                 var camel;
7803                 if(!(camel = propCache[prop])){
7804                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7805                 }
7806                 
7807                 if(camel == 'opacity') {
7808                     this.setOpacity(value);
7809                 }else{
7810                     this.dom.style[camel] = value;
7811                 }
7812             }else{
7813                 for(var style in prop){
7814                     if(typeof prop[style] != "function"){
7815                        this.setStyle(style, prop[style]);
7816                     }
7817                 }
7818             }
7819             return this;
7820         },
7821
7822         /**
7823          * More flexible version of {@link #setStyle} for setting style properties.
7824          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7825          * a function which returns such a specification.
7826          * @return {Roo.Element} this
7827          */
7828         applyStyles : function(style){
7829             Roo.DomHelper.applyStyles(this.dom, style);
7830             return this;
7831         },
7832
7833         /**
7834           * 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).
7835           * @return {Number} The X position of the element
7836           */
7837         getX : function(){
7838             return D.getX(this.dom);
7839         },
7840
7841         /**
7842           * 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).
7843           * @return {Number} The Y position of the element
7844           */
7845         getY : function(){
7846             return D.getY(this.dom);
7847         },
7848
7849         /**
7850           * 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).
7851           * @return {Array} The XY position of the element
7852           */
7853         getXY : function(){
7854             return D.getXY(this.dom);
7855         },
7856
7857         /**
7858          * 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).
7859          * @param {Number} The X position of the element
7860          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7861          * @return {Roo.Element} this
7862          */
7863         setX : function(x, animate){
7864             if(!animate || !A){
7865                 D.setX(this.dom, x);
7866             }else{
7867                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7868             }
7869             return this;
7870         },
7871
7872         /**
7873          * 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).
7874          * @param {Number} The Y position of the element
7875          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7876          * @return {Roo.Element} this
7877          */
7878         setY : function(y, animate){
7879             if(!animate || !A){
7880                 D.setY(this.dom, y);
7881             }else{
7882                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7883             }
7884             return this;
7885         },
7886
7887         /**
7888          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7889          * @param {String} left The left CSS property value
7890          * @return {Roo.Element} this
7891          */
7892         setLeft : function(left){
7893             this.setStyle("left", this.addUnits(left));
7894             return this;
7895         },
7896
7897         /**
7898          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7899          * @param {String} top The top CSS property value
7900          * @return {Roo.Element} this
7901          */
7902         setTop : function(top){
7903             this.setStyle("top", this.addUnits(top));
7904             return this;
7905         },
7906
7907         /**
7908          * Sets the element's CSS right style.
7909          * @param {String} right The right CSS property value
7910          * @return {Roo.Element} this
7911          */
7912         setRight : function(right){
7913             this.setStyle("right", this.addUnits(right));
7914             return this;
7915         },
7916
7917         /**
7918          * Sets the element's CSS bottom style.
7919          * @param {String} bottom The bottom CSS property value
7920          * @return {Roo.Element} this
7921          */
7922         setBottom : function(bottom){
7923             this.setStyle("bottom", this.addUnits(bottom));
7924             return this;
7925         },
7926
7927         /**
7928          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7929          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7930          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7931          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7932          * @return {Roo.Element} this
7933          */
7934         setXY : function(pos, animate){
7935             if(!animate || !A){
7936                 D.setXY(this.dom, pos);
7937             }else{
7938                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7939             }
7940             return this;
7941         },
7942
7943         /**
7944          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7945          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7946          * @param {Number} x X value for new position (coordinates are page-based)
7947          * @param {Number} y Y value for new position (coordinates are page-based)
7948          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7949          * @return {Roo.Element} this
7950          */
7951         setLocation : function(x, y, animate){
7952             this.setXY([x, y], this.preanim(arguments, 2));
7953             return this;
7954         },
7955
7956         /**
7957          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7958          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7959          * @param {Number} x X value for new position (coordinates are page-based)
7960          * @param {Number} y Y value for new position (coordinates are page-based)
7961          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7962          * @return {Roo.Element} this
7963          */
7964         moveTo : function(x, y, animate){
7965             this.setXY([x, y], this.preanim(arguments, 2));
7966             return this;
7967         },
7968
7969         /**
7970          * Returns the region of the given element.
7971          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7972          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7973          */
7974         getRegion : function(){
7975             return D.getRegion(this.dom);
7976         },
7977
7978         /**
7979          * Returns the offset height of the element
7980          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7981          * @return {Number} The element's height
7982          */
7983         getHeight : function(contentHeight){
7984             var h = this.dom.offsetHeight || 0;
7985             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7986         },
7987
7988         /**
7989          * Returns the offset width of the element
7990          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7991          * @return {Number} The element's width
7992          */
7993         getWidth : function(contentWidth){
7994             var w = this.dom.offsetWidth || 0;
7995             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7996         },
7997
7998         /**
7999          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8000          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8001          * if a height has not been set using CSS.
8002          * @return {Number}
8003          */
8004         getComputedHeight : function(){
8005             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8006             if(!h){
8007                 h = parseInt(this.getStyle('height'), 10) || 0;
8008                 if(!this.isBorderBox()){
8009                     h += this.getFrameWidth('tb');
8010                 }
8011             }
8012             return h;
8013         },
8014
8015         /**
8016          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8017          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8018          * if a width has not been set using CSS.
8019          * @return {Number}
8020          */
8021         getComputedWidth : function(){
8022             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8023             if(!w){
8024                 w = parseInt(this.getStyle('width'), 10) || 0;
8025                 if(!this.isBorderBox()){
8026                     w += this.getFrameWidth('lr');
8027                 }
8028             }
8029             return w;
8030         },
8031
8032         /**
8033          * Returns the size of the element.
8034          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8035          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8036          */
8037         getSize : function(contentSize){
8038             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8039         },
8040
8041         /**
8042          * Returns the width and height of the viewport.
8043          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8044          */
8045         getViewSize : function(){
8046             var d = this.dom, doc = document, aw = 0, ah = 0;
8047             if(d == doc || d == doc.body){
8048                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8049             }else{
8050                 return {
8051                     width : d.clientWidth,
8052                     height: d.clientHeight
8053                 };
8054             }
8055         },
8056
8057         /**
8058          * Returns the value of the "value" attribute
8059          * @param {Boolean} asNumber true to parse the value as a number
8060          * @return {String/Number}
8061          */
8062         getValue : function(asNumber){
8063             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8064         },
8065
8066         // private
8067         adjustWidth : function(width){
8068             if(typeof width == "number"){
8069                 if(this.autoBoxAdjust && !this.isBorderBox()){
8070                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8071                 }
8072                 if(width < 0){
8073                     width = 0;
8074                 }
8075             }
8076             return width;
8077         },
8078
8079         // private
8080         adjustHeight : function(height){
8081             if(typeof height == "number"){
8082                if(this.autoBoxAdjust && !this.isBorderBox()){
8083                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8084                }
8085                if(height < 0){
8086                    height = 0;
8087                }
8088             }
8089             return height;
8090         },
8091
8092         /**
8093          * Set the width of the element
8094          * @param {Number} width The new width
8095          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8096          * @return {Roo.Element} this
8097          */
8098         setWidth : function(width, animate){
8099             width = this.adjustWidth(width);
8100             if(!animate || !A){
8101                 this.dom.style.width = this.addUnits(width);
8102             }else{
8103                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8104             }
8105             return this;
8106         },
8107
8108         /**
8109          * Set the height of the element
8110          * @param {Number} height The new height
8111          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8112          * @return {Roo.Element} this
8113          */
8114          setHeight : function(height, animate){
8115             height = this.adjustHeight(height);
8116             if(!animate || !A){
8117                 this.dom.style.height = this.addUnits(height);
8118             }else{
8119                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8120             }
8121             return this;
8122         },
8123
8124         /**
8125          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8126          * @param {Number} width The new width
8127          * @param {Number} height The new height
8128          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8129          * @return {Roo.Element} this
8130          */
8131          setSize : function(width, height, animate){
8132             if(typeof width == "object"){ // in case of object from getSize()
8133                 height = width.height; width = width.width;
8134             }
8135             width = this.adjustWidth(width); height = this.adjustHeight(height);
8136             if(!animate || !A){
8137                 this.dom.style.width = this.addUnits(width);
8138                 this.dom.style.height = this.addUnits(height);
8139             }else{
8140                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8141             }
8142             return this;
8143         },
8144
8145         /**
8146          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8147          * @param {Number} x X value for new position (coordinates are page-based)
8148          * @param {Number} y Y value for new position (coordinates are page-based)
8149          * @param {Number} width The new width
8150          * @param {Number} height The new height
8151          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8152          * @return {Roo.Element} this
8153          */
8154         setBounds : function(x, y, width, height, animate){
8155             if(!animate || !A){
8156                 this.setSize(width, height);
8157                 this.setLocation(x, y);
8158             }else{
8159                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8160                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8161                               this.preanim(arguments, 4), 'motion');
8162             }
8163             return this;
8164         },
8165
8166         /**
8167          * 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.
8168          * @param {Roo.lib.Region} region The region to fill
8169          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8170          * @return {Roo.Element} this
8171          */
8172         setRegion : function(region, animate){
8173             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8174             return this;
8175         },
8176
8177         /**
8178          * Appends an event handler
8179          *
8180          * @param {String}   eventName     The type of event to append
8181          * @param {Function} fn        The method the event invokes
8182          * @param {Object} scope       (optional) The scope (this object) of the fn
8183          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8184          */
8185         addListener : function(eventName, fn, scope, options){
8186             if (this.dom) {
8187                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8188             }
8189         },
8190
8191         /**
8192          * Removes an event handler from this element
8193          * @param {String} eventName the type of event to remove
8194          * @param {Function} fn the method the event invokes
8195          * @return {Roo.Element} this
8196          */
8197         removeListener : function(eventName, fn){
8198             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8199             return this;
8200         },
8201
8202         /**
8203          * Removes all previous added listeners from this element
8204          * @return {Roo.Element} this
8205          */
8206         removeAllListeners : function(){
8207             E.purgeElement(this.dom);
8208             return this;
8209         },
8210
8211         relayEvent : function(eventName, observable){
8212             this.on(eventName, function(e){
8213                 observable.fireEvent(eventName, e);
8214             });
8215         },
8216
8217         /**
8218          * Set the opacity of the element
8219          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8220          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8221          * @return {Roo.Element} this
8222          */
8223          setOpacity : function(opacity, animate){
8224             if(!animate || !A){
8225                 var s = this.dom.style;
8226                 if(Roo.isIE){
8227                     s.zoom = 1;
8228                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8229                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8230                 }else{
8231                     s.opacity = opacity;
8232                 }
8233             }else{
8234                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8235             }
8236             return this;
8237         },
8238
8239         /**
8240          * Gets the left X coordinate
8241          * @param {Boolean} local True to get the local css position instead of page coordinate
8242          * @return {Number}
8243          */
8244         getLeft : function(local){
8245             if(!local){
8246                 return this.getX();
8247             }else{
8248                 return parseInt(this.getStyle("left"), 10) || 0;
8249             }
8250         },
8251
8252         /**
8253          * Gets the right X coordinate of the element (element X position + element width)
8254          * @param {Boolean} local True to get the local css position instead of page coordinate
8255          * @return {Number}
8256          */
8257         getRight : function(local){
8258             if(!local){
8259                 return this.getX() + this.getWidth();
8260             }else{
8261                 return (this.getLeft(true) + this.getWidth()) || 0;
8262             }
8263         },
8264
8265         /**
8266          * Gets the top Y coordinate
8267          * @param {Boolean} local True to get the local css position instead of page coordinate
8268          * @return {Number}
8269          */
8270         getTop : function(local) {
8271             if(!local){
8272                 return this.getY();
8273             }else{
8274                 return parseInt(this.getStyle("top"), 10) || 0;
8275             }
8276         },
8277
8278         /**
8279          * Gets the bottom Y coordinate of the element (element Y position + element height)
8280          * @param {Boolean} local True to get the local css position instead of page coordinate
8281          * @return {Number}
8282          */
8283         getBottom : function(local){
8284             if(!local){
8285                 return this.getY() + this.getHeight();
8286             }else{
8287                 return (this.getTop(true) + this.getHeight()) || 0;
8288             }
8289         },
8290
8291         /**
8292         * Initializes positioning on this element. If a desired position is not passed, it will make the
8293         * the element positioned relative IF it is not already positioned.
8294         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8295         * @param {Number} zIndex (optional) The zIndex to apply
8296         * @param {Number} x (optional) Set the page X position
8297         * @param {Number} y (optional) Set the page Y position
8298         */
8299         position : function(pos, zIndex, x, y){
8300             if(!pos){
8301                if(this.getStyle('position') == 'static'){
8302                    this.setStyle('position', 'relative');
8303                }
8304             }else{
8305                 this.setStyle("position", pos);
8306             }
8307             if(zIndex){
8308                 this.setStyle("z-index", zIndex);
8309             }
8310             if(x !== undefined && y !== undefined){
8311                 this.setXY([x, y]);
8312             }else if(x !== undefined){
8313                 this.setX(x);
8314             }else if(y !== undefined){
8315                 this.setY(y);
8316             }
8317         },
8318
8319         /**
8320         * Clear positioning back to the default when the document was loaded
8321         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8322         * @return {Roo.Element} this
8323          */
8324         clearPositioning : function(value){
8325             value = value ||'';
8326             this.setStyle({
8327                 "left": value,
8328                 "right": value,
8329                 "top": value,
8330                 "bottom": value,
8331                 "z-index": "",
8332                 "position" : "static"
8333             });
8334             return this;
8335         },
8336
8337         /**
8338         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8339         * snapshot before performing an update and then restoring the element.
8340         * @return {Object}
8341         */
8342         getPositioning : function(){
8343             var l = this.getStyle("left");
8344             var t = this.getStyle("top");
8345             return {
8346                 "position" : this.getStyle("position"),
8347                 "left" : l,
8348                 "right" : l ? "" : this.getStyle("right"),
8349                 "top" : t,
8350                 "bottom" : t ? "" : this.getStyle("bottom"),
8351                 "z-index" : this.getStyle("z-index")
8352             };
8353         },
8354
8355         /**
8356          * Gets the width of the border(s) for the specified side(s)
8357          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8358          * passing lr would get the border (l)eft width + the border (r)ight width.
8359          * @return {Number} The width of the sides passed added together
8360          */
8361         getBorderWidth : function(side){
8362             return this.addStyles(side, El.borders);
8363         },
8364
8365         /**
8366          * Gets the width of the padding(s) for the specified side(s)
8367          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8368          * passing lr would get the padding (l)eft + the padding (r)ight.
8369          * @return {Number} The padding of the sides passed added together
8370          */
8371         getPadding : function(side){
8372             return this.addStyles(side, El.paddings);
8373         },
8374
8375         /**
8376         * Set positioning with an object returned by getPositioning().
8377         * @param {Object} posCfg
8378         * @return {Roo.Element} this
8379          */
8380         setPositioning : function(pc){
8381             this.applyStyles(pc);
8382             if(pc.right == "auto"){
8383                 this.dom.style.right = "";
8384             }
8385             if(pc.bottom == "auto"){
8386                 this.dom.style.bottom = "";
8387             }
8388             return this;
8389         },
8390
8391         // private
8392         fixDisplay : function(){
8393             if(this.getStyle("display") == "none"){
8394                 this.setStyle("visibility", "hidden");
8395                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8396                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8397                     this.setStyle("display", "block");
8398                 }
8399             }
8400         },
8401
8402         /**
8403          * Quick set left and top adding default units
8404          * @param {String} left The left CSS property value
8405          * @param {String} top The top CSS property value
8406          * @return {Roo.Element} this
8407          */
8408          setLeftTop : function(left, top){
8409             this.dom.style.left = this.addUnits(left);
8410             this.dom.style.top = this.addUnits(top);
8411             return this;
8412         },
8413
8414         /**
8415          * Move this element relative to its current position.
8416          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8417          * @param {Number} distance How far to move the element in pixels
8418          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8419          * @return {Roo.Element} this
8420          */
8421          move : function(direction, distance, animate){
8422             var xy = this.getXY();
8423             direction = direction.toLowerCase();
8424             switch(direction){
8425                 case "l":
8426                 case "left":
8427                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8428                     break;
8429                case "r":
8430                case "right":
8431                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8432                     break;
8433                case "t":
8434                case "top":
8435                case "up":
8436                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8437                     break;
8438                case "b":
8439                case "bottom":
8440                case "down":
8441                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8442                     break;
8443             }
8444             return this;
8445         },
8446
8447         /**
8448          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8449          * @return {Roo.Element} this
8450          */
8451         clip : function(){
8452             if(!this.isClipped){
8453                this.isClipped = true;
8454                this.originalClip = {
8455                    "o": this.getStyle("overflow"),
8456                    "x": this.getStyle("overflow-x"),
8457                    "y": this.getStyle("overflow-y")
8458                };
8459                this.setStyle("overflow", "hidden");
8460                this.setStyle("overflow-x", "hidden");
8461                this.setStyle("overflow-y", "hidden");
8462             }
8463             return this;
8464         },
8465
8466         /**
8467          *  Return clipping (overflow) to original clipping before clip() was called
8468          * @return {Roo.Element} this
8469          */
8470         unclip : function(){
8471             if(this.isClipped){
8472                 this.isClipped = false;
8473                 var o = this.originalClip;
8474                 if(o.o){this.setStyle("overflow", o.o);}
8475                 if(o.x){this.setStyle("overflow-x", o.x);}
8476                 if(o.y){this.setStyle("overflow-y", o.y);}
8477             }
8478             return this;
8479         },
8480
8481
8482         /**
8483          * Gets the x,y coordinates specified by the anchor position on the element.
8484          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8485          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8486          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8487          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8488          * @return {Array} [x, y] An array containing the element's x and y coordinates
8489          */
8490         getAnchorXY : function(anchor, local, s){
8491             //Passing a different size is useful for pre-calculating anchors,
8492             //especially for anchored animations that change the el size.
8493
8494             var w, h, vp = false;
8495             if(!s){
8496                 var d = this.dom;
8497                 if(d == document.body || d == document){
8498                     vp = true;
8499                     w = D.getViewWidth(); h = D.getViewHeight();
8500                 }else{
8501                     w = this.getWidth(); h = this.getHeight();
8502                 }
8503             }else{
8504                 w = s.width;  h = s.height;
8505             }
8506             var x = 0, y = 0, r = Math.round;
8507             switch((anchor || "tl").toLowerCase()){
8508                 case "c":
8509                     x = r(w*.5);
8510                     y = r(h*.5);
8511                 break;
8512                 case "t":
8513                     x = r(w*.5);
8514                     y = 0;
8515                 break;
8516                 case "l":
8517                     x = 0;
8518                     y = r(h*.5);
8519                 break;
8520                 case "r":
8521                     x = w;
8522                     y = r(h*.5);
8523                 break;
8524                 case "b":
8525                     x = r(w*.5);
8526                     y = h;
8527                 break;
8528                 case "tl":
8529                     x = 0;
8530                     y = 0;
8531                 break;
8532                 case "bl":
8533                     x = 0;
8534                     y = h;
8535                 break;
8536                 case "br":
8537                     x = w;
8538                     y = h;
8539                 break;
8540                 case "tr":
8541                     x = w;
8542                     y = 0;
8543                 break;
8544             }
8545             if(local === true){
8546                 return [x, y];
8547             }
8548             if(vp){
8549                 var sc = this.getScroll();
8550                 return [x + sc.left, y + sc.top];
8551             }
8552             //Add the element's offset xy
8553             var o = this.getXY();
8554             return [x+o[0], y+o[1]];
8555         },
8556
8557         /**
8558          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8559          * supported position values.
8560          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8561          * @param {String} position The position to align to.
8562          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8563          * @return {Array} [x, y]
8564          */
8565         getAlignToXY : function(el, p, o){
8566             el = Roo.get(el);
8567             var d = this.dom;
8568             if(!el.dom){
8569                 throw "Element.alignTo with an element that doesn't exist";
8570             }
8571             var c = false; //constrain to viewport
8572             var p1 = "", p2 = "";
8573             o = o || [0,0];
8574
8575             if(!p){
8576                 p = "tl-bl";
8577             }else if(p == "?"){
8578                 p = "tl-bl?";
8579             }else if(p.indexOf("-") == -1){
8580                 p = "tl-" + p;
8581             }
8582             p = p.toLowerCase();
8583             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8584             if(!m){
8585                throw "Element.alignTo with an invalid alignment " + p;
8586             }
8587             p1 = m[1]; p2 = m[2]; c = !!m[3];
8588
8589             //Subtract the aligned el's internal xy from the target's offset xy
8590             //plus custom offset to get the aligned el's new offset xy
8591             var a1 = this.getAnchorXY(p1, true);
8592             var a2 = el.getAnchorXY(p2, false);
8593             var x = a2[0] - a1[0] + o[0];
8594             var y = a2[1] - a1[1] + o[1];
8595             if(c){
8596                 //constrain the aligned el to viewport if necessary
8597                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8598                 // 5px of margin for ie
8599                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8600
8601                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8602                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8603                 //otherwise swap the aligned el to the opposite border of the target.
8604                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8605                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8606                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8607                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8608
8609                var doc = document;
8610                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8611                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8612
8613                if((x+w) > dw + scrollX){
8614                     x = swapX ? r.left-w : dw+scrollX-w;
8615                 }
8616                if(x < scrollX){
8617                    x = swapX ? r.right : scrollX;
8618                }
8619                if((y+h) > dh + scrollY){
8620                     y = swapY ? r.top-h : dh+scrollY-h;
8621                 }
8622                if (y < scrollY){
8623                    y = swapY ? r.bottom : scrollY;
8624                }
8625             }
8626             return [x,y];
8627         },
8628
8629         // private
8630         getConstrainToXY : function(){
8631             var os = {top:0, left:0, bottom:0, right: 0};
8632
8633             return function(el, local, offsets, proposedXY){
8634                 el = Roo.get(el);
8635                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8636
8637                 var vw, vh, vx = 0, vy = 0;
8638                 if(el.dom == document.body || el.dom == document){
8639                     vw = Roo.lib.Dom.getViewWidth();
8640                     vh = Roo.lib.Dom.getViewHeight();
8641                 }else{
8642                     vw = el.dom.clientWidth;
8643                     vh = el.dom.clientHeight;
8644                     if(!local){
8645                         var vxy = el.getXY();
8646                         vx = vxy[0];
8647                         vy = vxy[1];
8648                     }
8649                 }
8650
8651                 var s = el.getScroll();
8652
8653                 vx += offsets.left + s.left;
8654                 vy += offsets.top + s.top;
8655
8656                 vw -= offsets.right;
8657                 vh -= offsets.bottom;
8658
8659                 var vr = vx+vw;
8660                 var vb = vy+vh;
8661
8662                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8663                 var x = xy[0], y = xy[1];
8664                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8665
8666                 // only move it if it needs it
8667                 var moved = false;
8668
8669                 // first validate right/bottom
8670                 if((x + w) > vr){
8671                     x = vr - w;
8672                     moved = true;
8673                 }
8674                 if((y + h) > vb){
8675                     y = vb - h;
8676                     moved = true;
8677                 }
8678                 // then make sure top/left isn't negative
8679                 if(x < vx){
8680                     x = vx;
8681                     moved = true;
8682                 }
8683                 if(y < vy){
8684                     y = vy;
8685                     moved = true;
8686                 }
8687                 return moved ? [x, y] : false;
8688             };
8689         }(),
8690
8691         // private
8692         adjustForConstraints : function(xy, parent, offsets){
8693             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8694         },
8695
8696         /**
8697          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8698          * document it aligns it to the viewport.
8699          * The position parameter is optional, and can be specified in any one of the following formats:
8700          * <ul>
8701          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8702          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8703          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8704          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8705          *   <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
8706          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8707          * </ul>
8708          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8709          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8710          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8711          * that specified in order to enforce the viewport constraints.
8712          * Following are all of the supported anchor positions:
8713     <pre>
8714     Value  Description
8715     -----  -----------------------------
8716     tl     The top left corner (default)
8717     t      The center of the top edge
8718     tr     The top right corner
8719     l      The center of the left edge
8720     c      In the center of the element
8721     r      The center of the right edge
8722     bl     The bottom left corner
8723     b      The center of the bottom edge
8724     br     The bottom right corner
8725     </pre>
8726     Example Usage:
8727     <pre><code>
8728     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8729     el.alignTo("other-el");
8730
8731     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8732     el.alignTo("other-el", "tr?");
8733
8734     // align the bottom right corner of el with the center left edge of other-el
8735     el.alignTo("other-el", "br-l?");
8736
8737     // align the center of el with the bottom left corner of other-el and
8738     // adjust the x position by -6 pixels (and the y position by 0)
8739     el.alignTo("other-el", "c-bl", [-6, 0]);
8740     </code></pre>
8741          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8742          * @param {String} position The position to align to.
8743          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8744          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8745          * @return {Roo.Element} this
8746          */
8747         alignTo : function(element, position, offsets, animate){
8748             var xy = this.getAlignToXY(element, position, offsets);
8749             this.setXY(xy, this.preanim(arguments, 3));
8750             return this;
8751         },
8752
8753         /**
8754          * Anchors an element to another element and realigns it when the window is resized.
8755          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8756          * @param {String} position The position to align to.
8757          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8758          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8759          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8760          * is a number, it is used as the buffer delay (defaults to 50ms).
8761          * @param {Function} callback The function to call after the animation finishes
8762          * @return {Roo.Element} this
8763          */
8764         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8765             var action = function(){
8766                 this.alignTo(el, alignment, offsets, animate);
8767                 Roo.callback(callback, this);
8768             };
8769             Roo.EventManager.onWindowResize(action, this);
8770             var tm = typeof monitorScroll;
8771             if(tm != 'undefined'){
8772                 Roo.EventManager.on(window, 'scroll', action, this,
8773                     {buffer: tm == 'number' ? monitorScroll : 50});
8774             }
8775             action.call(this); // align immediately
8776             return this;
8777         },
8778         /**
8779          * Clears any opacity settings from this element. Required in some cases for IE.
8780          * @return {Roo.Element} this
8781          */
8782         clearOpacity : function(){
8783             if (window.ActiveXObject) {
8784                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8785                     this.dom.style.filter = "";
8786                 }
8787             } else {
8788                 this.dom.style.opacity = "";
8789                 this.dom.style["-moz-opacity"] = "";
8790                 this.dom.style["-khtml-opacity"] = "";
8791             }
8792             return this;
8793         },
8794
8795         /**
8796          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8797          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8798          * @return {Roo.Element} this
8799          */
8800         hide : function(animate){
8801             this.setVisible(false, this.preanim(arguments, 0));
8802             return this;
8803         },
8804
8805         /**
8806         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8807         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8808          * @return {Roo.Element} this
8809          */
8810         show : function(animate){
8811             this.setVisible(true, this.preanim(arguments, 0));
8812             return this;
8813         },
8814
8815         /**
8816          * @private Test if size has a unit, otherwise appends the default
8817          */
8818         addUnits : function(size){
8819             return Roo.Element.addUnits(size, this.defaultUnit);
8820         },
8821
8822         /**
8823          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8824          * @return {Roo.Element} this
8825          */
8826         beginMeasure : function(){
8827             var el = this.dom;
8828             if(el.offsetWidth || el.offsetHeight){
8829                 return this; // offsets work already
8830             }
8831             var changed = [];
8832             var p = this.dom, b = document.body; // start with this element
8833             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8834                 var pe = Roo.get(p);
8835                 if(pe.getStyle('display') == 'none'){
8836                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8837                     p.style.visibility = "hidden";
8838                     p.style.display = "block";
8839                 }
8840                 p = p.parentNode;
8841             }
8842             this._measureChanged = changed;
8843             return this;
8844
8845         },
8846
8847         /**
8848          * Restores displays to before beginMeasure was called
8849          * @return {Roo.Element} this
8850          */
8851         endMeasure : function(){
8852             var changed = this._measureChanged;
8853             if(changed){
8854                 for(var i = 0, len = changed.length; i < len; i++) {
8855                     var r = changed[i];
8856                     r.el.style.visibility = r.visibility;
8857                     r.el.style.display = "none";
8858                 }
8859                 this._measureChanged = null;
8860             }
8861             return this;
8862         },
8863
8864         /**
8865         * Update the innerHTML of this element, optionally searching for and processing scripts
8866         * @param {String} html The new HTML
8867         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8868         * @param {Function} callback For async script loading you can be noticed when the update completes
8869         * @return {Roo.Element} this
8870          */
8871         update : function(html, loadScripts, callback){
8872             if(typeof html == "undefined"){
8873                 html = "";
8874             }
8875             if(loadScripts !== true){
8876                 this.dom.innerHTML = html;
8877                 if(typeof callback == "function"){
8878                     callback();
8879                 }
8880                 return this;
8881             }
8882             var id = Roo.id();
8883             var dom = this.dom;
8884
8885             html += '<span id="' + id + '"></span>';
8886
8887             E.onAvailable(id, function(){
8888                 var hd = document.getElementsByTagName("head")[0];
8889                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8890                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8891                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8892
8893                 var match;
8894                 while(match = re.exec(html)){
8895                     var attrs = match[1];
8896                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8897                     if(srcMatch && srcMatch[2]){
8898                        var s = document.createElement("script");
8899                        s.src = srcMatch[2];
8900                        var typeMatch = attrs.match(typeRe);
8901                        if(typeMatch && typeMatch[2]){
8902                            s.type = typeMatch[2];
8903                        }
8904                        hd.appendChild(s);
8905                     }else if(match[2] && match[2].length > 0){
8906                         if(window.execScript) {
8907                            window.execScript(match[2]);
8908                         } else {
8909                             /**
8910                              * eval:var:id
8911                              * eval:var:dom
8912                              * eval:var:html
8913                              * 
8914                              */
8915                            window.eval(match[2]);
8916                         }
8917                     }
8918                 }
8919                 var el = document.getElementById(id);
8920                 if(el){el.parentNode.removeChild(el);}
8921                 if(typeof callback == "function"){
8922                     callback();
8923                 }
8924             });
8925             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8926             return this;
8927         },
8928
8929         /**
8930          * Direct access to the UpdateManager update() method (takes the same parameters).
8931          * @param {String/Function} url The url for this request or a function to call to get the url
8932          * @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}
8933          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8934          * @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.
8935          * @return {Roo.Element} this
8936          */
8937         load : function(){
8938             var um = this.getUpdateManager();
8939             um.update.apply(um, arguments);
8940             return this;
8941         },
8942
8943         /**
8944         * Gets this element's UpdateManager
8945         * @return {Roo.UpdateManager} The UpdateManager
8946         */
8947         getUpdateManager : function(){
8948             if(!this.updateManager){
8949                 this.updateManager = new Roo.UpdateManager(this);
8950             }
8951             return this.updateManager;
8952         },
8953
8954         /**
8955          * Disables text selection for this element (normalized across browsers)
8956          * @return {Roo.Element} this
8957          */
8958         unselectable : function(){
8959             this.dom.unselectable = "on";
8960             this.swallowEvent("selectstart", true);
8961             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8962             this.addClass("x-unselectable");
8963             return this;
8964         },
8965
8966         /**
8967         * Calculates the x, y to center this element on the screen
8968         * @return {Array} The x, y values [x, y]
8969         */
8970         getCenterXY : function(){
8971             return this.getAlignToXY(document, 'c-c');
8972         },
8973
8974         /**
8975         * Centers the Element in either the viewport, or another Element.
8976         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8977         */
8978         center : function(centerIn){
8979             this.alignTo(centerIn || document, 'c-c');
8980             return this;
8981         },
8982
8983         /**
8984          * Tests various css rules/browsers to determine if this element uses a border box
8985          * @return {Boolean}
8986          */
8987         isBorderBox : function(){
8988             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8989         },
8990
8991         /**
8992          * Return a box {x, y, width, height} that can be used to set another elements
8993          * size/location to match this element.
8994          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8995          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8996          * @return {Object} box An object in the format {x, y, width, height}
8997          */
8998         getBox : function(contentBox, local){
8999             var xy;
9000             if(!local){
9001                 xy = this.getXY();
9002             }else{
9003                 var left = parseInt(this.getStyle("left"), 10) || 0;
9004                 var top = parseInt(this.getStyle("top"), 10) || 0;
9005                 xy = [left, top];
9006             }
9007             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9008             if(!contentBox){
9009                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9010             }else{
9011                 var l = this.getBorderWidth("l")+this.getPadding("l");
9012                 var r = this.getBorderWidth("r")+this.getPadding("r");
9013                 var t = this.getBorderWidth("t")+this.getPadding("t");
9014                 var b = this.getBorderWidth("b")+this.getPadding("b");
9015                 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)};
9016             }
9017             bx.right = bx.x + bx.width;
9018             bx.bottom = bx.y + bx.height;
9019             return bx;
9020         },
9021
9022         /**
9023          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9024          for more information about the sides.
9025          * @param {String} sides
9026          * @return {Number}
9027          */
9028         getFrameWidth : function(sides, onlyContentBox){
9029             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9030         },
9031
9032         /**
9033          * 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.
9034          * @param {Object} box The box to fill {x, y, width, height}
9035          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9036          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9037          * @return {Roo.Element} this
9038          */
9039         setBox : function(box, adjust, animate){
9040             var w = box.width, h = box.height;
9041             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9042                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9043                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9044             }
9045             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9046             return this;
9047         },
9048
9049         /**
9050          * Forces the browser to repaint this element
9051          * @return {Roo.Element} this
9052          */
9053          repaint : function(){
9054             var dom = this.dom;
9055             this.addClass("x-repaint");
9056             setTimeout(function(){
9057                 Roo.get(dom).removeClass("x-repaint");
9058             }, 1);
9059             return this;
9060         },
9061
9062         /**
9063          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9064          * then it returns the calculated width of the sides (see getPadding)
9065          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9066          * @return {Object/Number}
9067          */
9068         getMargins : function(side){
9069             if(!side){
9070                 return {
9071                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9072                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9073                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9074                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9075                 };
9076             }else{
9077                 return this.addStyles(side, El.margins);
9078              }
9079         },
9080
9081         // private
9082         addStyles : function(sides, styles){
9083             var val = 0, v, w;
9084             for(var i = 0, len = sides.length; i < len; i++){
9085                 v = this.getStyle(styles[sides.charAt(i)]);
9086                 if(v){
9087                      w = parseInt(v, 10);
9088                      if(w){ val += w; }
9089                 }
9090             }
9091             return val;
9092         },
9093
9094         /**
9095          * Creates a proxy element of this element
9096          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9097          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9098          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9099          * @return {Roo.Element} The new proxy element
9100          */
9101         createProxy : function(config, renderTo, matchBox){
9102             if(renderTo){
9103                 renderTo = Roo.getDom(renderTo);
9104             }else{
9105                 renderTo = document.body;
9106             }
9107             config = typeof config == "object" ?
9108                 config : {tag : "div", cls: config};
9109             var proxy = Roo.DomHelper.append(renderTo, config, true);
9110             if(matchBox){
9111                proxy.setBox(this.getBox());
9112             }
9113             return proxy;
9114         },
9115
9116         /**
9117          * Puts a mask over this element to disable user interaction. Requires core.css.
9118          * This method can only be applied to elements which accept child nodes.
9119          * @param {String} msg (optional) A message to display in the mask
9120          * @param {String} msgCls (optional) A css class to apply to the msg element
9121          * @return {Element} The mask  element
9122          */
9123         mask : function(msg, msgCls)
9124         {
9125             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9126                 this.setStyle("position", "relative");
9127             }
9128             if(!this._mask){
9129                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9130             }
9131             
9132             this.addClass("x-masked");
9133             this._mask.setDisplayed(true);
9134             
9135             // we wander
9136             var z = 0;
9137             var dom = this.dom;
9138             while (dom && dom.style) {
9139                 if (!isNaN(parseInt(dom.style.zIndex))) {
9140                     z = Math.max(z, parseInt(dom.style.zIndex));
9141                 }
9142                 dom = dom.parentNode;
9143             }
9144             // if we are masking the body - then it hides everything..
9145             if (this.dom == document.body) {
9146                 z = 1000000;
9147                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9148                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9149             }
9150            
9151             if(typeof msg == 'string'){
9152                 if(!this._maskMsg){
9153                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9154                         cls: "roo-el-mask-msg", 
9155                         cn: [
9156                             {
9157                                 tag: 'i',
9158                                 cls: 'fa fa-spinner fa-spin'
9159                             },
9160                             {
9161                                 tag: 'div'
9162                             }   
9163                         ]
9164                     }, true);
9165                 }
9166                 var mm = this._maskMsg;
9167                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9168                 if (mm.dom.lastChild) { // weird IE issue?
9169                     mm.dom.lastChild.innerHTML = msg;
9170                 }
9171                 mm.setDisplayed(true);
9172                 mm.center(this);
9173                 mm.setStyle('z-index', z + 102);
9174             }
9175             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9176                 this._mask.setHeight(this.getHeight());
9177             }
9178             this._mask.setStyle('z-index', z + 100);
9179             
9180             return this._mask;
9181         },
9182
9183         /**
9184          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9185          * it is cached for reuse.
9186          */
9187         unmask : function(removeEl){
9188             if(this._mask){
9189                 if(removeEl === true){
9190                     this._mask.remove();
9191                     delete this._mask;
9192                     if(this._maskMsg){
9193                         this._maskMsg.remove();
9194                         delete this._maskMsg;
9195                     }
9196                 }else{
9197                     this._mask.setDisplayed(false);
9198                     if(this._maskMsg){
9199                         this._maskMsg.setDisplayed(false);
9200                     }
9201                 }
9202             }
9203             this.removeClass("x-masked");
9204         },
9205
9206         /**
9207          * Returns true if this element is masked
9208          * @return {Boolean}
9209          */
9210         isMasked : function(){
9211             return this._mask && this._mask.isVisible();
9212         },
9213
9214         /**
9215          * Creates an iframe shim for this element to keep selects and other windowed objects from
9216          * showing through.
9217          * @return {Roo.Element} The new shim element
9218          */
9219         createShim : function(){
9220             var el = document.createElement('iframe');
9221             el.frameBorder = 'no';
9222             el.className = 'roo-shim';
9223             if(Roo.isIE && Roo.isSecure){
9224                 el.src = Roo.SSL_SECURE_URL;
9225             }
9226             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9227             shim.autoBoxAdjust = false;
9228             return shim;
9229         },
9230
9231         /**
9232          * Removes this element from the DOM and deletes it from the cache
9233          */
9234         remove : function(){
9235             if(this.dom.parentNode){
9236                 this.dom.parentNode.removeChild(this.dom);
9237             }
9238             delete El.cache[this.dom.id];
9239         },
9240
9241         /**
9242          * Sets up event handlers to add and remove a css class when the mouse is over this element
9243          * @param {String} className
9244          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9245          * mouseout events for children elements
9246          * @return {Roo.Element} this
9247          */
9248         addClassOnOver : function(className, preventFlicker){
9249             this.on("mouseover", function(){
9250                 Roo.fly(this, '_internal').addClass(className);
9251             }, this.dom);
9252             var removeFn = function(e){
9253                 if(preventFlicker !== true || !e.within(this, true)){
9254                     Roo.fly(this, '_internal').removeClass(className);
9255                 }
9256             };
9257             this.on("mouseout", removeFn, this.dom);
9258             return this;
9259         },
9260
9261         /**
9262          * Sets up event handlers to add and remove a css class when this element has the focus
9263          * @param {String} className
9264          * @return {Roo.Element} this
9265          */
9266         addClassOnFocus : function(className){
9267             this.on("focus", function(){
9268                 Roo.fly(this, '_internal').addClass(className);
9269             }, this.dom);
9270             this.on("blur", function(){
9271                 Roo.fly(this, '_internal').removeClass(className);
9272             }, this.dom);
9273             return this;
9274         },
9275         /**
9276          * 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)
9277          * @param {String} className
9278          * @return {Roo.Element} this
9279          */
9280         addClassOnClick : function(className){
9281             var dom = this.dom;
9282             this.on("mousedown", function(){
9283                 Roo.fly(dom, '_internal').addClass(className);
9284                 var d = Roo.get(document);
9285                 var fn = function(){
9286                     Roo.fly(dom, '_internal').removeClass(className);
9287                     d.removeListener("mouseup", fn);
9288                 };
9289                 d.on("mouseup", fn);
9290             });
9291             return this;
9292         },
9293
9294         /**
9295          * Stops the specified event from bubbling and optionally prevents the default action
9296          * @param {String} eventName
9297          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9298          * @return {Roo.Element} this
9299          */
9300         swallowEvent : function(eventName, preventDefault){
9301             var fn = function(e){
9302                 e.stopPropagation();
9303                 if(preventDefault){
9304                     e.preventDefault();
9305                 }
9306             };
9307             if(eventName instanceof Array){
9308                 for(var i = 0, len = eventName.length; i < len; i++){
9309                      this.on(eventName[i], fn);
9310                 }
9311                 return this;
9312             }
9313             this.on(eventName, fn);
9314             return this;
9315         },
9316
9317         /**
9318          * @private
9319          */
9320       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9321
9322         /**
9323          * Sizes this element to its parent element's dimensions performing
9324          * neccessary box adjustments.
9325          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9326          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9327          * @return {Roo.Element} this
9328          */
9329         fitToParent : function(monitorResize, targetParent) {
9330           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9331           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9332           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9333             return;
9334           }
9335           var p = Roo.get(targetParent || this.dom.parentNode);
9336           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9337           if (monitorResize === true) {
9338             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9339             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9340           }
9341           return this;
9342         },
9343
9344         /**
9345          * Gets the next sibling, skipping text nodes
9346          * @return {HTMLElement} The next sibling or null
9347          */
9348         getNextSibling : function(){
9349             var n = this.dom.nextSibling;
9350             while(n && n.nodeType != 1){
9351                 n = n.nextSibling;
9352             }
9353             return n;
9354         },
9355
9356         /**
9357          * Gets the previous sibling, skipping text nodes
9358          * @return {HTMLElement} The previous sibling or null
9359          */
9360         getPrevSibling : function(){
9361             var n = this.dom.previousSibling;
9362             while(n && n.nodeType != 1){
9363                 n = n.previousSibling;
9364             }
9365             return n;
9366         },
9367
9368
9369         /**
9370          * Appends the passed element(s) to this element
9371          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9372          * @return {Roo.Element} this
9373          */
9374         appendChild: function(el){
9375             el = Roo.get(el);
9376             el.appendTo(this);
9377             return this;
9378         },
9379
9380         /**
9381          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9382          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9383          * automatically generated with the specified attributes.
9384          * @param {HTMLElement} insertBefore (optional) a child element of this element
9385          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9386          * @return {Roo.Element} The new child element
9387          */
9388         createChild: function(config, insertBefore, returnDom){
9389             config = config || {tag:'div'};
9390             if(insertBefore){
9391                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9392             }
9393             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9394         },
9395
9396         /**
9397          * Appends this element to the passed element
9398          * @param {String/HTMLElement/Element} el The new parent element
9399          * @return {Roo.Element} this
9400          */
9401         appendTo: function(el){
9402             el = Roo.getDom(el);
9403             el.appendChild(this.dom);
9404             return this;
9405         },
9406
9407         /**
9408          * Inserts this element before the passed element in the DOM
9409          * @param {String/HTMLElement/Element} el The element to insert before
9410          * @return {Roo.Element} this
9411          */
9412         insertBefore: function(el){
9413             el = Roo.getDom(el);
9414             el.parentNode.insertBefore(this.dom, el);
9415             return this;
9416         },
9417
9418         /**
9419          * Inserts this element after the passed element in the DOM
9420          * @param {String/HTMLElement/Element} el The element to insert after
9421          * @return {Roo.Element} this
9422          */
9423         insertAfter: function(el){
9424             el = Roo.getDom(el);
9425             el.parentNode.insertBefore(this.dom, el.nextSibling);
9426             return this;
9427         },
9428
9429         /**
9430          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9431          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9432          * @return {Roo.Element} The new child
9433          */
9434         insertFirst: function(el, returnDom){
9435             el = el || {};
9436             if(typeof el == 'object' && !el.nodeType){ // dh config
9437                 return this.createChild(el, this.dom.firstChild, returnDom);
9438             }else{
9439                 el = Roo.getDom(el);
9440                 this.dom.insertBefore(el, this.dom.firstChild);
9441                 return !returnDom ? Roo.get(el) : el;
9442             }
9443         },
9444
9445         /**
9446          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9447          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9448          * @param {String} where (optional) 'before' or 'after' defaults to before
9449          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9450          * @return {Roo.Element} the inserted Element
9451          */
9452         insertSibling: function(el, where, returnDom){
9453             where = where ? where.toLowerCase() : 'before';
9454             el = el || {};
9455             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9456
9457             if(typeof el == 'object' && !el.nodeType){ // dh config
9458                 if(where == 'after' && !this.dom.nextSibling){
9459                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9460                 }else{
9461                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9462                 }
9463
9464             }else{
9465                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9466                             where == 'before' ? this.dom : this.dom.nextSibling);
9467                 if(!returnDom){
9468                     rt = Roo.get(rt);
9469                 }
9470             }
9471             return rt;
9472         },
9473
9474         /**
9475          * Creates and wraps this element with another element
9476          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9477          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9478          * @return {HTMLElement/Element} The newly created wrapper element
9479          */
9480         wrap: function(config, returnDom){
9481             if(!config){
9482                 config = {tag: "div"};
9483             }
9484             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9485             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9486             return newEl;
9487         },
9488
9489         /**
9490          * Replaces the passed element with this element
9491          * @param {String/HTMLElement/Element} el The element to replace
9492          * @return {Roo.Element} this
9493          */
9494         replace: function(el){
9495             el = Roo.get(el);
9496             this.insertBefore(el);
9497             el.remove();
9498             return this;
9499         },
9500
9501         /**
9502          * Inserts an html fragment into this element
9503          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9504          * @param {String} html The HTML fragment
9505          * @param {Boolean} returnEl True to return an Roo.Element
9506          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9507          */
9508         insertHtml : function(where, html, returnEl){
9509             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9510             return returnEl ? Roo.get(el) : el;
9511         },
9512
9513         /**
9514          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9515          * @param {Object} o The object with the attributes
9516          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9517          * @return {Roo.Element} this
9518          */
9519         set : function(o, useSet){
9520             var el = this.dom;
9521             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9522             for(var attr in o){
9523                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9524                 if(attr=="cls"){
9525                     el.className = o["cls"];
9526                 }else{
9527                     if(useSet) {
9528                         el.setAttribute(attr, o[attr]);
9529                     } else {
9530                         el[attr] = o[attr];
9531                     }
9532                 }
9533             }
9534             if(o.style){
9535                 Roo.DomHelper.applyStyles(el, o.style);
9536             }
9537             return this;
9538         },
9539
9540         /**
9541          * Convenience method for constructing a KeyMap
9542          * @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:
9543          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9544          * @param {Function} fn The function to call
9545          * @param {Object} scope (optional) The scope of the function
9546          * @return {Roo.KeyMap} The KeyMap created
9547          */
9548         addKeyListener : function(key, fn, scope){
9549             var config;
9550             if(typeof key != "object" || key instanceof Array){
9551                 config = {
9552                     key: key,
9553                     fn: fn,
9554                     scope: scope
9555                 };
9556             }else{
9557                 config = {
9558                     key : key.key,
9559                     shift : key.shift,
9560                     ctrl : key.ctrl,
9561                     alt : key.alt,
9562                     fn: fn,
9563                     scope: scope
9564                 };
9565             }
9566             return new Roo.KeyMap(this, config);
9567         },
9568
9569         /**
9570          * Creates a KeyMap for this element
9571          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9572          * @return {Roo.KeyMap} The KeyMap created
9573          */
9574         addKeyMap : function(config){
9575             return new Roo.KeyMap(this, config);
9576         },
9577
9578         /**
9579          * Returns true if this element is scrollable.
9580          * @return {Boolean}
9581          */
9582          isScrollable : function(){
9583             var dom = this.dom;
9584             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9585         },
9586
9587         /**
9588          * 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().
9589          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9590          * @param {Number} value The new scroll value
9591          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9592          * @return {Element} this
9593          */
9594
9595         scrollTo : function(side, value, animate){
9596             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9597             if(!animate || !A){
9598                 this.dom[prop] = value;
9599             }else{
9600                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9601                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9602             }
9603             return this;
9604         },
9605
9606         /**
9607          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9608          * within this element's scrollable range.
9609          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9610          * @param {Number} distance How far to scroll the element in pixels
9611          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9612          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9613          * was scrolled as far as it could go.
9614          */
9615          scroll : function(direction, distance, animate){
9616              if(!this.isScrollable()){
9617                  return;
9618              }
9619              var el = this.dom;
9620              var l = el.scrollLeft, t = el.scrollTop;
9621              var w = el.scrollWidth, h = el.scrollHeight;
9622              var cw = el.clientWidth, ch = el.clientHeight;
9623              direction = direction.toLowerCase();
9624              var scrolled = false;
9625              var a = this.preanim(arguments, 2);
9626              switch(direction){
9627                  case "l":
9628                  case "left":
9629                      if(w - l > cw){
9630                          var v = Math.min(l + distance, w-cw);
9631                          this.scrollTo("left", v, a);
9632                          scrolled = true;
9633                      }
9634                      break;
9635                 case "r":
9636                 case "right":
9637                      if(l > 0){
9638                          var v = Math.max(l - distance, 0);
9639                          this.scrollTo("left", v, a);
9640                          scrolled = true;
9641                      }
9642                      break;
9643                 case "t":
9644                 case "top":
9645                 case "up":
9646                      if(t > 0){
9647                          var v = Math.max(t - distance, 0);
9648                          this.scrollTo("top", v, a);
9649                          scrolled = true;
9650                      }
9651                      break;
9652                 case "b":
9653                 case "bottom":
9654                 case "down":
9655                      if(h - t > ch){
9656                          var v = Math.min(t + distance, h-ch);
9657                          this.scrollTo("top", v, a);
9658                          scrolled = true;
9659                      }
9660                      break;
9661              }
9662              return scrolled;
9663         },
9664
9665         /**
9666          * Translates the passed page coordinates into left/top css values for this element
9667          * @param {Number/Array} x The page x or an array containing [x, y]
9668          * @param {Number} y The page y
9669          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9670          */
9671         translatePoints : function(x, y){
9672             if(typeof x == 'object' || x instanceof Array){
9673                 y = x[1]; x = x[0];
9674             }
9675             var p = this.getStyle('position');
9676             var o = this.getXY();
9677
9678             var l = parseInt(this.getStyle('left'), 10);
9679             var t = parseInt(this.getStyle('top'), 10);
9680
9681             if(isNaN(l)){
9682                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9683             }
9684             if(isNaN(t)){
9685                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9686             }
9687
9688             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9689         },
9690
9691         /**
9692          * Returns the current scroll position of the element.
9693          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9694          */
9695         getScroll : function(){
9696             var d = this.dom, doc = document;
9697             if(d == doc || d == doc.body){
9698                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9699                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9700                 return {left: l, top: t};
9701             }else{
9702                 return {left: d.scrollLeft, top: d.scrollTop};
9703             }
9704         },
9705
9706         /**
9707          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9708          * are convert to standard 6 digit hex color.
9709          * @param {String} attr The css attribute
9710          * @param {String} defaultValue The default value to use when a valid color isn't found
9711          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9712          * YUI color anims.
9713          */
9714         getColor : function(attr, defaultValue, prefix){
9715             var v = this.getStyle(attr);
9716             if(!v || v == "transparent" || v == "inherit") {
9717                 return defaultValue;
9718             }
9719             var color = typeof prefix == "undefined" ? "#" : prefix;
9720             if(v.substr(0, 4) == "rgb("){
9721                 var rvs = v.slice(4, v.length -1).split(",");
9722                 for(var i = 0; i < 3; i++){
9723                     var h = parseInt(rvs[i]).toString(16);
9724                     if(h < 16){
9725                         h = "0" + h;
9726                     }
9727                     color += h;
9728                 }
9729             } else {
9730                 if(v.substr(0, 1) == "#"){
9731                     if(v.length == 4) {
9732                         for(var i = 1; i < 4; i++){
9733                             var c = v.charAt(i);
9734                             color +=  c + c;
9735                         }
9736                     }else if(v.length == 7){
9737                         color += v.substr(1);
9738                     }
9739                 }
9740             }
9741             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9742         },
9743
9744         /**
9745          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9746          * gradient background, rounded corners and a 4-way shadow.
9747          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9748          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9749          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9750          * @return {Roo.Element} this
9751          */
9752         boxWrap : function(cls){
9753             cls = cls || 'x-box';
9754             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9755             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9756             return el;
9757         },
9758
9759         /**
9760          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9761          * @param {String} namespace The namespace in which to look for the attribute
9762          * @param {String} name The attribute name
9763          * @return {String} The attribute value
9764          */
9765         getAttributeNS : Roo.isIE ? function(ns, name){
9766             var d = this.dom;
9767             var type = typeof d[ns+":"+name];
9768             if(type != 'undefined' && type != 'unknown'){
9769                 return d[ns+":"+name];
9770             }
9771             return d[name];
9772         } : function(ns, name){
9773             var d = this.dom;
9774             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9775         },
9776         
9777         
9778         /**
9779          * Sets or Returns the value the dom attribute value
9780          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9781          * @param {String} value (optional) The value to set the attribute to
9782          * @return {String} The attribute value
9783          */
9784         attr : function(name){
9785             if (arguments.length > 1) {
9786                 this.dom.setAttribute(name, arguments[1]);
9787                 return arguments[1];
9788             }
9789             if (typeof(name) == 'object') {
9790                 for(var i in name) {
9791                     this.attr(i, name[i]);
9792                 }
9793                 return name;
9794             }
9795             
9796             
9797             if (!this.dom.hasAttribute(name)) {
9798                 return undefined;
9799             }
9800             return this.dom.getAttribute(name);
9801         }
9802         
9803         
9804         
9805     };
9806
9807     var ep = El.prototype;
9808
9809     /**
9810      * Appends an event handler (Shorthand for addListener)
9811      * @param {String}   eventName     The type of event to append
9812      * @param {Function} fn        The method the event invokes
9813      * @param {Object} scope       (optional) The scope (this object) of the fn
9814      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9815      * @method
9816      */
9817     ep.on = ep.addListener;
9818         // backwards compat
9819     ep.mon = ep.addListener;
9820
9821     /**
9822      * Removes an event handler from this element (shorthand for removeListener)
9823      * @param {String} eventName the type of event to remove
9824      * @param {Function} fn the method the event invokes
9825      * @return {Roo.Element} this
9826      * @method
9827      */
9828     ep.un = ep.removeListener;
9829
9830     /**
9831      * true to automatically adjust width and height settings for box-model issues (default to true)
9832      */
9833     ep.autoBoxAdjust = true;
9834
9835     // private
9836     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9837
9838     // private
9839     El.addUnits = function(v, defaultUnit){
9840         if(v === "" || v == "auto"){
9841             return v;
9842         }
9843         if(v === undefined){
9844             return '';
9845         }
9846         if(typeof v == "number" || !El.unitPattern.test(v)){
9847             return v + (defaultUnit || 'px');
9848         }
9849         return v;
9850     };
9851
9852     // special markup used throughout Roo when box wrapping elements
9853     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>';
9854     /**
9855      * Visibility mode constant - Use visibility to hide element
9856      * @static
9857      * @type Number
9858      */
9859     El.VISIBILITY = 1;
9860     /**
9861      * Visibility mode constant - Use display to hide element
9862      * @static
9863      * @type Number
9864      */
9865     El.DISPLAY = 2;
9866
9867     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9868     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9869     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9870
9871
9872
9873     /**
9874      * @private
9875      */
9876     El.cache = {};
9877
9878     var docEl;
9879
9880     /**
9881      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9882      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9883      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9884      * @return {Element} The Element object
9885      * @static
9886      */
9887     El.get = function(el){
9888         var ex, elm, id;
9889         if(!el){ return null; }
9890         if(typeof el == "string"){ // element id
9891             if(!(elm = document.getElementById(el))){
9892                 return null;
9893             }
9894             if(ex = El.cache[el]){
9895                 ex.dom = elm;
9896             }else{
9897                 ex = El.cache[el] = new El(elm);
9898             }
9899             return ex;
9900         }else if(el.tagName){ // dom element
9901             if(!(id = el.id)){
9902                 id = Roo.id(el);
9903             }
9904             if(ex = El.cache[id]){
9905                 ex.dom = el;
9906             }else{
9907                 ex = El.cache[id] = new El(el);
9908             }
9909             return ex;
9910         }else if(el instanceof El){
9911             if(el != docEl){
9912                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9913                                                               // catch case where it hasn't been appended
9914                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9915             }
9916             return el;
9917         }else if(el.isComposite){
9918             return el;
9919         }else if(el instanceof Array){
9920             return El.select(el);
9921         }else if(el == document){
9922             // create a bogus element object representing the document object
9923             if(!docEl){
9924                 var f = function(){};
9925                 f.prototype = El.prototype;
9926                 docEl = new f();
9927                 docEl.dom = document;
9928             }
9929             return docEl;
9930         }
9931         return null;
9932     };
9933
9934     // private
9935     El.uncache = function(el){
9936         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9937             if(a[i]){
9938                 delete El.cache[a[i].id || a[i]];
9939             }
9940         }
9941     };
9942
9943     // private
9944     // Garbage collection - uncache elements/purge listeners on orphaned elements
9945     // so we don't hold a reference and cause the browser to retain them
9946     El.garbageCollect = function(){
9947         if(!Roo.enableGarbageCollector){
9948             clearInterval(El.collectorThread);
9949             return;
9950         }
9951         for(var eid in El.cache){
9952             var el = El.cache[eid], d = el.dom;
9953             // -------------------------------------------------------
9954             // Determining what is garbage:
9955             // -------------------------------------------------------
9956             // !d
9957             // dom node is null, definitely garbage
9958             // -------------------------------------------------------
9959             // !d.parentNode
9960             // no parentNode == direct orphan, definitely garbage
9961             // -------------------------------------------------------
9962             // !d.offsetParent && !document.getElementById(eid)
9963             // display none elements have no offsetParent so we will
9964             // also try to look it up by it's id. However, check
9965             // offsetParent first so we don't do unneeded lookups.
9966             // This enables collection of elements that are not orphans
9967             // directly, but somewhere up the line they have an orphan
9968             // parent.
9969             // -------------------------------------------------------
9970             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9971                 delete El.cache[eid];
9972                 if(d && Roo.enableListenerCollection){
9973                     E.purgeElement(d);
9974                 }
9975             }
9976         }
9977     }
9978     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9979
9980
9981     // dom is optional
9982     El.Flyweight = function(dom){
9983         this.dom = dom;
9984     };
9985     El.Flyweight.prototype = El.prototype;
9986
9987     El._flyweights = {};
9988     /**
9989      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9990      * the dom node can be overwritten by other code.
9991      * @param {String/HTMLElement} el The dom node or id
9992      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9993      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9994      * @static
9995      * @return {Element} The shared Element object
9996      */
9997     El.fly = function(el, named){
9998         named = named || '_global';
9999         el = Roo.getDom(el);
10000         if(!el){
10001             return null;
10002         }
10003         if(!El._flyweights[named]){
10004             El._flyweights[named] = new El.Flyweight();
10005         }
10006         El._flyweights[named].dom = el;
10007         return El._flyweights[named];
10008     };
10009
10010     /**
10011      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10012      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10013      * Shorthand of {@link Roo.Element#get}
10014      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10015      * @return {Element} The Element object
10016      * @member Roo
10017      * @method get
10018      */
10019     Roo.get = El.get;
10020     /**
10021      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10022      * the dom node can be overwritten by other code.
10023      * Shorthand of {@link Roo.Element#fly}
10024      * @param {String/HTMLElement} el The dom node or id
10025      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10026      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10027      * @static
10028      * @return {Element} The shared Element object
10029      * @member Roo
10030      * @method fly
10031      */
10032     Roo.fly = El.fly;
10033
10034     // speedy lookup for elements never to box adjust
10035     var noBoxAdjust = Roo.isStrict ? {
10036         select:1
10037     } : {
10038         input:1, select:1, textarea:1
10039     };
10040     if(Roo.isIE || Roo.isGecko){
10041         noBoxAdjust['button'] = 1;
10042     }
10043
10044
10045     Roo.EventManager.on(window, 'unload', function(){
10046         delete El.cache;
10047         delete El._flyweights;
10048     });
10049 })();
10050
10051
10052
10053
10054 if(Roo.DomQuery){
10055     Roo.Element.selectorFunction = Roo.DomQuery.select;
10056 }
10057
10058 Roo.Element.select = function(selector, unique, root){
10059     var els;
10060     if(typeof selector == "string"){
10061         els = Roo.Element.selectorFunction(selector, root);
10062     }else if(selector.length !== undefined){
10063         els = selector;
10064     }else{
10065         throw "Invalid selector";
10066     }
10067     if(unique === true){
10068         return new Roo.CompositeElement(els);
10069     }else{
10070         return new Roo.CompositeElementLite(els);
10071     }
10072 };
10073 /**
10074  * Selects elements based on the passed CSS selector to enable working on them as 1.
10075  * @param {String/Array} selector The CSS selector or an array of elements
10076  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10077  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10078  * @return {CompositeElementLite/CompositeElement}
10079  * @member Roo
10080  * @method select
10081  */
10082 Roo.select = Roo.Element.select;
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095
10096
10097 /*
10098  * Based on:
10099  * Ext JS Library 1.1.1
10100  * Copyright(c) 2006-2007, Ext JS, LLC.
10101  *
10102  * Originally Released Under LGPL - original licence link has changed is not relivant.
10103  *
10104  * Fork - LGPL
10105  * <script type="text/javascript">
10106  */
10107
10108
10109
10110 //Notifies Element that fx methods are available
10111 Roo.enableFx = true;
10112
10113 /**
10114  * @class Roo.Fx
10115  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10116  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10117  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10118  * Element effects to work.</p><br/>
10119  *
10120  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10121  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10122  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10123  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10124  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10125  * expected results and should be done with care.</p><br/>
10126  *
10127  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10128  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10129 <pre>
10130 Value  Description
10131 -----  -----------------------------
10132 tl     The top left corner
10133 t      The center of the top edge
10134 tr     The top right corner
10135 l      The center of the left edge
10136 r      The center of the right edge
10137 bl     The bottom left corner
10138 b      The center of the bottom edge
10139 br     The bottom right corner
10140 </pre>
10141  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10142  * below are common options that can be passed to any Fx method.</b>
10143  * @cfg {Function} callback A function called when the effect is finished
10144  * @cfg {Object} scope The scope of the effect function
10145  * @cfg {String} easing A valid Easing value for the effect
10146  * @cfg {String} afterCls A css class to apply after the effect
10147  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10148  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10149  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10150  * effects that end with the element being visually hidden, ignored otherwise)
10151  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10152  * a function which returns such a specification that will be applied to the Element after the effect finishes
10153  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10154  * @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
10155  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10156  */
10157 Roo.Fx = {
10158         /**
10159          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10160          * origin for the slide effect.  This function automatically handles wrapping the element with
10161          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10162          * Usage:
10163          *<pre><code>
10164 // default: slide the element in from the top
10165 el.slideIn();
10166
10167 // custom: slide the element in from the right with a 2-second duration
10168 el.slideIn('r', { duration: 2 });
10169
10170 // common config options shown with default values
10171 el.slideIn('t', {
10172     easing: 'easeOut',
10173     duration: .5
10174 });
10175 </code></pre>
10176          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10177          * @param {Object} options (optional) Object literal with any of the Fx config options
10178          * @return {Roo.Element} The Element
10179          */
10180     slideIn : function(anchor, o){
10181         var el = this.getFxEl();
10182         o = o || {};
10183
10184         el.queueFx(o, function(){
10185
10186             anchor = anchor || "t";
10187
10188             // fix display to visibility
10189             this.fixDisplay();
10190
10191             // restore values after effect
10192             var r = this.getFxRestore();
10193             var b = this.getBox();
10194             // fixed size for slide
10195             this.setSize(b);
10196
10197             // wrap if needed
10198             var wrap = this.fxWrap(r.pos, o, "hidden");
10199
10200             var st = this.dom.style;
10201             st.visibility = "visible";
10202             st.position = "absolute";
10203
10204             // clear out temp styles after slide and unwrap
10205             var after = function(){
10206                 el.fxUnwrap(wrap, r.pos, o);
10207                 st.width = r.width;
10208                 st.height = r.height;
10209                 el.afterFx(o);
10210             };
10211             // time to calc the positions
10212             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10213
10214             switch(anchor.toLowerCase()){
10215                 case "t":
10216                     wrap.setSize(b.width, 0);
10217                     st.left = st.bottom = "0";
10218                     a = {height: bh};
10219                 break;
10220                 case "l":
10221                     wrap.setSize(0, b.height);
10222                     st.right = st.top = "0";
10223                     a = {width: bw};
10224                 break;
10225                 case "r":
10226                     wrap.setSize(0, b.height);
10227                     wrap.setX(b.right);
10228                     st.left = st.top = "0";
10229                     a = {width: bw, points: pt};
10230                 break;
10231                 case "b":
10232                     wrap.setSize(b.width, 0);
10233                     wrap.setY(b.bottom);
10234                     st.left = st.top = "0";
10235                     a = {height: bh, points: pt};
10236                 break;
10237                 case "tl":
10238                     wrap.setSize(0, 0);
10239                     st.right = st.bottom = "0";
10240                     a = {width: bw, height: bh};
10241                 break;
10242                 case "bl":
10243                     wrap.setSize(0, 0);
10244                     wrap.setY(b.y+b.height);
10245                     st.right = st.top = "0";
10246                     a = {width: bw, height: bh, points: pt};
10247                 break;
10248                 case "br":
10249                     wrap.setSize(0, 0);
10250                     wrap.setXY([b.right, b.bottom]);
10251                     st.left = st.top = "0";
10252                     a = {width: bw, height: bh, points: pt};
10253                 break;
10254                 case "tr":
10255                     wrap.setSize(0, 0);
10256                     wrap.setX(b.x+b.width);
10257                     st.left = st.bottom = "0";
10258                     a = {width: bw, height: bh, points: pt};
10259                 break;
10260             }
10261             this.dom.style.visibility = "visible";
10262             wrap.show();
10263
10264             arguments.callee.anim = wrap.fxanim(a,
10265                 o,
10266                 'motion',
10267                 .5,
10268                 'easeOut', after);
10269         });
10270         return this;
10271     },
10272     
10273         /**
10274          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10275          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10276          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10277          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10278          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10279          * Usage:
10280          *<pre><code>
10281 // default: slide the element out to the top
10282 el.slideOut();
10283
10284 // custom: slide the element out to the right with a 2-second duration
10285 el.slideOut('r', { duration: 2 });
10286
10287 // common config options shown with default values
10288 el.slideOut('t', {
10289     easing: 'easeOut',
10290     duration: .5,
10291     remove: false,
10292     useDisplay: false
10293 });
10294 </code></pre>
10295          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10296          * @param {Object} options (optional) Object literal with any of the Fx config options
10297          * @return {Roo.Element} The Element
10298          */
10299     slideOut : function(anchor, o){
10300         var el = this.getFxEl();
10301         o = o || {};
10302
10303         el.queueFx(o, function(){
10304
10305             anchor = anchor || "t";
10306
10307             // restore values after effect
10308             var r = this.getFxRestore();
10309             
10310             var b = this.getBox();
10311             // fixed size for slide
10312             this.setSize(b);
10313
10314             // wrap if needed
10315             var wrap = this.fxWrap(r.pos, o, "visible");
10316
10317             var st = this.dom.style;
10318             st.visibility = "visible";
10319             st.position = "absolute";
10320
10321             wrap.setSize(b);
10322
10323             var after = function(){
10324                 if(o.useDisplay){
10325                     el.setDisplayed(false);
10326                 }else{
10327                     el.hide();
10328                 }
10329
10330                 el.fxUnwrap(wrap, r.pos, o);
10331
10332                 st.width = r.width;
10333                 st.height = r.height;
10334
10335                 el.afterFx(o);
10336             };
10337
10338             var a, zero = {to: 0};
10339             switch(anchor.toLowerCase()){
10340                 case "t":
10341                     st.left = st.bottom = "0";
10342                     a = {height: zero};
10343                 break;
10344                 case "l":
10345                     st.right = st.top = "0";
10346                     a = {width: zero};
10347                 break;
10348                 case "r":
10349                     st.left = st.top = "0";
10350                     a = {width: zero, points: {to:[b.right, b.y]}};
10351                 break;
10352                 case "b":
10353                     st.left = st.top = "0";
10354                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10355                 break;
10356                 case "tl":
10357                     st.right = st.bottom = "0";
10358                     a = {width: zero, height: zero};
10359                 break;
10360                 case "bl":
10361                     st.right = st.top = "0";
10362                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10363                 break;
10364                 case "br":
10365                     st.left = st.top = "0";
10366                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10367                 break;
10368                 case "tr":
10369                     st.left = st.bottom = "0";
10370                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10371                 break;
10372             }
10373
10374             arguments.callee.anim = wrap.fxanim(a,
10375                 o,
10376                 'motion',
10377                 .5,
10378                 "easeOut", after);
10379         });
10380         return this;
10381     },
10382
10383         /**
10384          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10385          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10386          * The element must be removed from the DOM using the 'remove' config option if desired.
10387          * Usage:
10388          *<pre><code>
10389 // default
10390 el.puff();
10391
10392 // common config options shown with default values
10393 el.puff({
10394     easing: 'easeOut',
10395     duration: .5,
10396     remove: false,
10397     useDisplay: false
10398 });
10399 </code></pre>
10400          * @param {Object} options (optional) Object literal with any of the Fx config options
10401          * @return {Roo.Element} The Element
10402          */
10403     puff : function(o){
10404         var el = this.getFxEl();
10405         o = o || {};
10406
10407         el.queueFx(o, function(){
10408             this.clearOpacity();
10409             this.show();
10410
10411             // restore values after effect
10412             var r = this.getFxRestore();
10413             var st = this.dom.style;
10414
10415             var after = function(){
10416                 if(o.useDisplay){
10417                     el.setDisplayed(false);
10418                 }else{
10419                     el.hide();
10420                 }
10421
10422                 el.clearOpacity();
10423
10424                 el.setPositioning(r.pos);
10425                 st.width = r.width;
10426                 st.height = r.height;
10427                 st.fontSize = '';
10428                 el.afterFx(o);
10429             };
10430
10431             var width = this.getWidth();
10432             var height = this.getHeight();
10433
10434             arguments.callee.anim = this.fxanim({
10435                     width : {to: this.adjustWidth(width * 2)},
10436                     height : {to: this.adjustHeight(height * 2)},
10437                     points : {by: [-(width * .5), -(height * .5)]},
10438                     opacity : {to: 0},
10439                     fontSize: {to:200, unit: "%"}
10440                 },
10441                 o,
10442                 'motion',
10443                 .5,
10444                 "easeOut", after);
10445         });
10446         return this;
10447     },
10448
10449         /**
10450          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10451          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10452          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10453          * Usage:
10454          *<pre><code>
10455 // default
10456 el.switchOff();
10457
10458 // all config options shown with default values
10459 el.switchOff({
10460     easing: 'easeIn',
10461     duration: .3,
10462     remove: false,
10463     useDisplay: false
10464 });
10465 </code></pre>
10466          * @param {Object} options (optional) Object literal with any of the Fx config options
10467          * @return {Roo.Element} The Element
10468          */
10469     switchOff : function(o){
10470         var el = this.getFxEl();
10471         o = o || {};
10472
10473         el.queueFx(o, function(){
10474             this.clearOpacity();
10475             this.clip();
10476
10477             // restore values after effect
10478             var r = this.getFxRestore();
10479             var st = this.dom.style;
10480
10481             var after = function(){
10482                 if(o.useDisplay){
10483                     el.setDisplayed(false);
10484                 }else{
10485                     el.hide();
10486                 }
10487
10488                 el.clearOpacity();
10489                 el.setPositioning(r.pos);
10490                 st.width = r.width;
10491                 st.height = r.height;
10492
10493                 el.afterFx(o);
10494             };
10495
10496             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10497                 this.clearOpacity();
10498                 (function(){
10499                     this.fxanim({
10500                         height:{to:1},
10501                         points:{by:[0, this.getHeight() * .5]}
10502                     }, o, 'motion', 0.3, 'easeIn', after);
10503                 }).defer(100, this);
10504             });
10505         });
10506         return this;
10507     },
10508
10509     /**
10510      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10511      * changed using the "attr" config option) and then fading back to the original color. If no original
10512      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10513      * Usage:
10514 <pre><code>
10515 // default: highlight background to yellow
10516 el.highlight();
10517
10518 // custom: highlight foreground text to blue for 2 seconds
10519 el.highlight("0000ff", { attr: 'color', duration: 2 });
10520
10521 // common config options shown with default values
10522 el.highlight("ffff9c", {
10523     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10524     endColor: (current color) or "ffffff",
10525     easing: 'easeIn',
10526     duration: 1
10527 });
10528 </code></pre>
10529      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10530      * @param {Object} options (optional) Object literal with any of the Fx config options
10531      * @return {Roo.Element} The Element
10532      */ 
10533     highlight : function(color, o){
10534         var el = this.getFxEl();
10535         o = o || {};
10536
10537         el.queueFx(o, function(){
10538             color = color || "ffff9c";
10539             attr = o.attr || "backgroundColor";
10540
10541             this.clearOpacity();
10542             this.show();
10543
10544             var origColor = this.getColor(attr);
10545             var restoreColor = this.dom.style[attr];
10546             endColor = (o.endColor || origColor) || "ffffff";
10547
10548             var after = function(){
10549                 el.dom.style[attr] = restoreColor;
10550                 el.afterFx(o);
10551             };
10552
10553             var a = {};
10554             a[attr] = {from: color, to: endColor};
10555             arguments.callee.anim = this.fxanim(a,
10556                 o,
10557                 'color',
10558                 1,
10559                 'easeIn', after);
10560         });
10561         return this;
10562     },
10563
10564    /**
10565     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10566     * Usage:
10567 <pre><code>
10568 // default: a single light blue ripple
10569 el.frame();
10570
10571 // custom: 3 red ripples lasting 3 seconds total
10572 el.frame("ff0000", 3, { duration: 3 });
10573
10574 // common config options shown with default values
10575 el.frame("C3DAF9", 1, {
10576     duration: 1 //duration of entire animation (not each individual ripple)
10577     // Note: Easing is not configurable and will be ignored if included
10578 });
10579 </code></pre>
10580     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10581     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10582     * @param {Object} options (optional) Object literal with any of the Fx config options
10583     * @return {Roo.Element} The Element
10584     */
10585     frame : function(color, count, o){
10586         var el = this.getFxEl();
10587         o = o || {};
10588
10589         el.queueFx(o, function(){
10590             color = color || "#C3DAF9";
10591             if(color.length == 6){
10592                 color = "#" + color;
10593             }
10594             count = count || 1;
10595             duration = o.duration || 1;
10596             this.show();
10597
10598             var b = this.getBox();
10599             var animFn = function(){
10600                 var proxy = this.createProxy({
10601
10602                      style:{
10603                         visbility:"hidden",
10604                         position:"absolute",
10605                         "z-index":"35000", // yee haw
10606                         border:"0px solid " + color
10607                      }
10608                   });
10609                 var scale = Roo.isBorderBox ? 2 : 1;
10610                 proxy.animate({
10611                     top:{from:b.y, to:b.y - 20},
10612                     left:{from:b.x, to:b.x - 20},
10613                     borderWidth:{from:0, to:10},
10614                     opacity:{from:1, to:0},
10615                     height:{from:b.height, to:(b.height + (20*scale))},
10616                     width:{from:b.width, to:(b.width + (20*scale))}
10617                 }, duration, function(){
10618                     proxy.remove();
10619                 });
10620                 if(--count > 0){
10621                      animFn.defer((duration/2)*1000, this);
10622                 }else{
10623                     el.afterFx(o);
10624                 }
10625             };
10626             animFn.call(this);
10627         });
10628         return this;
10629     },
10630
10631    /**
10632     * Creates a pause before any subsequent queued effects begin.  If there are
10633     * no effects queued after the pause it will have no effect.
10634     * Usage:
10635 <pre><code>
10636 el.pause(1);
10637 </code></pre>
10638     * @param {Number} seconds The length of time to pause (in seconds)
10639     * @return {Roo.Element} The Element
10640     */
10641     pause : function(seconds){
10642         var el = this.getFxEl();
10643         var o = {};
10644
10645         el.queueFx(o, function(){
10646             setTimeout(function(){
10647                 el.afterFx(o);
10648             }, seconds * 1000);
10649         });
10650         return this;
10651     },
10652
10653    /**
10654     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10655     * using the "endOpacity" config option.
10656     * Usage:
10657 <pre><code>
10658 // default: fade in from opacity 0 to 100%
10659 el.fadeIn();
10660
10661 // custom: fade in from opacity 0 to 75% over 2 seconds
10662 el.fadeIn({ endOpacity: .75, duration: 2});
10663
10664 // common config options shown with default values
10665 el.fadeIn({
10666     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10667     easing: 'easeOut',
10668     duration: .5
10669 });
10670 </code></pre>
10671     * @param {Object} options (optional) Object literal with any of the Fx config options
10672     * @return {Roo.Element} The Element
10673     */
10674     fadeIn : function(o){
10675         var el = this.getFxEl();
10676         o = o || {};
10677         el.queueFx(o, function(){
10678             this.setOpacity(0);
10679             this.fixDisplay();
10680             this.dom.style.visibility = 'visible';
10681             var to = o.endOpacity || 1;
10682             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10683                 o, null, .5, "easeOut", function(){
10684                 if(to == 1){
10685                     this.clearOpacity();
10686                 }
10687                 el.afterFx(o);
10688             });
10689         });
10690         return this;
10691     },
10692
10693    /**
10694     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10695     * using the "endOpacity" config option.
10696     * Usage:
10697 <pre><code>
10698 // default: fade out from the element's current opacity to 0
10699 el.fadeOut();
10700
10701 // custom: fade out from the element's current opacity to 25% over 2 seconds
10702 el.fadeOut({ endOpacity: .25, duration: 2});
10703
10704 // common config options shown with default values
10705 el.fadeOut({
10706     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10707     easing: 'easeOut',
10708     duration: .5
10709     remove: false,
10710     useDisplay: false
10711 });
10712 </code></pre>
10713     * @param {Object} options (optional) Object literal with any of the Fx config options
10714     * @return {Roo.Element} The Element
10715     */
10716     fadeOut : function(o){
10717         var el = this.getFxEl();
10718         o = o || {};
10719         el.queueFx(o, function(){
10720             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10721                 o, null, .5, "easeOut", function(){
10722                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10723                      this.dom.style.display = "none";
10724                 }else{
10725                      this.dom.style.visibility = "hidden";
10726                 }
10727                 this.clearOpacity();
10728                 el.afterFx(o);
10729             });
10730         });
10731         return this;
10732     },
10733
10734    /**
10735     * Animates the transition of an element's dimensions from a starting height/width
10736     * to an ending height/width.
10737     * Usage:
10738 <pre><code>
10739 // change height and width to 100x100 pixels
10740 el.scale(100, 100);
10741
10742 // common config options shown with default values.  The height and width will default to
10743 // the element's existing values if passed as null.
10744 el.scale(
10745     [element's width],
10746     [element's height], {
10747     easing: 'easeOut',
10748     duration: .35
10749 });
10750 </code></pre>
10751     * @param {Number} width  The new width (pass undefined to keep the original width)
10752     * @param {Number} height  The new height (pass undefined to keep the original height)
10753     * @param {Object} options (optional) Object literal with any of the Fx config options
10754     * @return {Roo.Element} The Element
10755     */
10756     scale : function(w, h, o){
10757         this.shift(Roo.apply({}, o, {
10758             width: w,
10759             height: h
10760         }));
10761         return this;
10762     },
10763
10764    /**
10765     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10766     * Any of these properties not specified in the config object will not be changed.  This effect 
10767     * requires that at least one new dimension, position or opacity setting must be passed in on
10768     * the config object in order for the function to have any effect.
10769     * Usage:
10770 <pre><code>
10771 // slide the element horizontally to x position 200 while changing the height and opacity
10772 el.shift({ x: 200, height: 50, opacity: .8 });
10773
10774 // common config options shown with default values.
10775 el.shift({
10776     width: [element's width],
10777     height: [element's height],
10778     x: [element's x position],
10779     y: [element's y position],
10780     opacity: [element's opacity],
10781     easing: 'easeOut',
10782     duration: .35
10783 });
10784 </code></pre>
10785     * @param {Object} options  Object literal with any of the Fx config options
10786     * @return {Roo.Element} The Element
10787     */
10788     shift : function(o){
10789         var el = this.getFxEl();
10790         o = o || {};
10791         el.queueFx(o, function(){
10792             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10793             if(w !== undefined){
10794                 a.width = {to: this.adjustWidth(w)};
10795             }
10796             if(h !== undefined){
10797                 a.height = {to: this.adjustHeight(h)};
10798             }
10799             if(x !== undefined || y !== undefined){
10800                 a.points = {to: [
10801                     x !== undefined ? x : this.getX(),
10802                     y !== undefined ? y : this.getY()
10803                 ]};
10804             }
10805             if(op !== undefined){
10806                 a.opacity = {to: op};
10807             }
10808             if(o.xy !== undefined){
10809                 a.points = {to: o.xy};
10810             }
10811             arguments.callee.anim = this.fxanim(a,
10812                 o, 'motion', .35, "easeOut", function(){
10813                 el.afterFx(o);
10814             });
10815         });
10816         return this;
10817     },
10818
10819         /**
10820          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10821          * ending point of the effect.
10822          * Usage:
10823          *<pre><code>
10824 // default: slide the element downward while fading out
10825 el.ghost();
10826
10827 // custom: slide the element out to the right with a 2-second duration
10828 el.ghost('r', { duration: 2 });
10829
10830 // common config options shown with default values
10831 el.ghost('b', {
10832     easing: 'easeOut',
10833     duration: .5
10834     remove: false,
10835     useDisplay: false
10836 });
10837 </code></pre>
10838          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10839          * @param {Object} options (optional) Object literal with any of the Fx config options
10840          * @return {Roo.Element} The Element
10841          */
10842     ghost : function(anchor, o){
10843         var el = this.getFxEl();
10844         o = o || {};
10845
10846         el.queueFx(o, function(){
10847             anchor = anchor || "b";
10848
10849             // restore values after effect
10850             var r = this.getFxRestore();
10851             var w = this.getWidth(),
10852                 h = this.getHeight();
10853
10854             var st = this.dom.style;
10855
10856             var after = function(){
10857                 if(o.useDisplay){
10858                     el.setDisplayed(false);
10859                 }else{
10860                     el.hide();
10861                 }
10862
10863                 el.clearOpacity();
10864                 el.setPositioning(r.pos);
10865                 st.width = r.width;
10866                 st.height = r.height;
10867
10868                 el.afterFx(o);
10869             };
10870
10871             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10872             switch(anchor.toLowerCase()){
10873                 case "t":
10874                     pt.by = [0, -h];
10875                 break;
10876                 case "l":
10877                     pt.by = [-w, 0];
10878                 break;
10879                 case "r":
10880                     pt.by = [w, 0];
10881                 break;
10882                 case "b":
10883                     pt.by = [0, h];
10884                 break;
10885                 case "tl":
10886                     pt.by = [-w, -h];
10887                 break;
10888                 case "bl":
10889                     pt.by = [-w, h];
10890                 break;
10891                 case "br":
10892                     pt.by = [w, h];
10893                 break;
10894                 case "tr":
10895                     pt.by = [w, -h];
10896                 break;
10897             }
10898
10899             arguments.callee.anim = this.fxanim(a,
10900                 o,
10901                 'motion',
10902                 .5,
10903                 "easeOut", after);
10904         });
10905         return this;
10906     },
10907
10908         /**
10909          * Ensures that all effects queued after syncFx is called on the element are
10910          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10911          * @return {Roo.Element} The Element
10912          */
10913     syncFx : function(){
10914         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10915             block : false,
10916             concurrent : true,
10917             stopFx : false
10918         });
10919         return this;
10920     },
10921
10922         /**
10923          * Ensures that all effects queued after sequenceFx is called on the element are
10924          * run in sequence.  This is the opposite of {@link #syncFx}.
10925          * @return {Roo.Element} The Element
10926          */
10927     sequenceFx : function(){
10928         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10929             block : false,
10930             concurrent : false,
10931             stopFx : false
10932         });
10933         return this;
10934     },
10935
10936         /* @private */
10937     nextFx : function(){
10938         var ef = this.fxQueue[0];
10939         if(ef){
10940             ef.call(this);
10941         }
10942     },
10943
10944         /**
10945          * Returns true if the element has any effects actively running or queued, else returns false.
10946          * @return {Boolean} True if element has active effects, else false
10947          */
10948     hasActiveFx : function(){
10949         return this.fxQueue && this.fxQueue[0];
10950     },
10951
10952         /**
10953          * Stops any running effects and clears the element's internal effects queue if it contains
10954          * any additional effects that haven't started yet.
10955          * @return {Roo.Element} The Element
10956          */
10957     stopFx : function(){
10958         if(this.hasActiveFx()){
10959             var cur = this.fxQueue[0];
10960             if(cur && cur.anim && cur.anim.isAnimated()){
10961                 this.fxQueue = [cur]; // clear out others
10962                 cur.anim.stop(true);
10963             }
10964         }
10965         return this;
10966     },
10967
10968         /* @private */
10969     beforeFx : function(o){
10970         if(this.hasActiveFx() && !o.concurrent){
10971            if(o.stopFx){
10972                this.stopFx();
10973                return true;
10974            }
10975            return false;
10976         }
10977         return true;
10978     },
10979
10980         /**
10981          * Returns true if the element is currently blocking so that no other effect can be queued
10982          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10983          * used to ensure that an effect initiated by a user action runs to completion prior to the
10984          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10985          * @return {Boolean} True if blocking, else false
10986          */
10987     hasFxBlock : function(){
10988         var q = this.fxQueue;
10989         return q && q[0] && q[0].block;
10990     },
10991
10992         /* @private */
10993     queueFx : function(o, fn){
10994         if(!this.fxQueue){
10995             this.fxQueue = [];
10996         }
10997         if(!this.hasFxBlock()){
10998             Roo.applyIf(o, this.fxDefaults);
10999             if(!o.concurrent){
11000                 var run = this.beforeFx(o);
11001                 fn.block = o.block;
11002                 this.fxQueue.push(fn);
11003                 if(run){
11004                     this.nextFx();
11005                 }
11006             }else{
11007                 fn.call(this);
11008             }
11009         }
11010         return this;
11011     },
11012
11013         /* @private */
11014     fxWrap : function(pos, o, vis){
11015         var wrap;
11016         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11017             var wrapXY;
11018             if(o.fixPosition){
11019                 wrapXY = this.getXY();
11020             }
11021             var div = document.createElement("div");
11022             div.style.visibility = vis;
11023             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11024             wrap.setPositioning(pos);
11025             if(wrap.getStyle("position") == "static"){
11026                 wrap.position("relative");
11027             }
11028             this.clearPositioning('auto');
11029             wrap.clip();
11030             wrap.dom.appendChild(this.dom);
11031             if(wrapXY){
11032                 wrap.setXY(wrapXY);
11033             }
11034         }
11035         return wrap;
11036     },
11037
11038         /* @private */
11039     fxUnwrap : function(wrap, pos, o){
11040         this.clearPositioning();
11041         this.setPositioning(pos);
11042         if(!o.wrap){
11043             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11044             wrap.remove();
11045         }
11046     },
11047
11048         /* @private */
11049     getFxRestore : function(){
11050         var st = this.dom.style;
11051         return {pos: this.getPositioning(), width: st.width, height : st.height};
11052     },
11053
11054         /* @private */
11055     afterFx : function(o){
11056         if(o.afterStyle){
11057             this.applyStyles(o.afterStyle);
11058         }
11059         if(o.afterCls){
11060             this.addClass(o.afterCls);
11061         }
11062         if(o.remove === true){
11063             this.remove();
11064         }
11065         Roo.callback(o.callback, o.scope, [this]);
11066         if(!o.concurrent){
11067             this.fxQueue.shift();
11068             this.nextFx();
11069         }
11070     },
11071
11072         /* @private */
11073     getFxEl : function(){ // support for composite element fx
11074         return Roo.get(this.dom);
11075     },
11076
11077         /* @private */
11078     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11079         animType = animType || 'run';
11080         opt = opt || {};
11081         var anim = Roo.lib.Anim[animType](
11082             this.dom, args,
11083             (opt.duration || defaultDur) || .35,
11084             (opt.easing || defaultEase) || 'easeOut',
11085             function(){
11086                 Roo.callback(cb, this);
11087             },
11088             this
11089         );
11090         opt.anim = anim;
11091         return anim;
11092     }
11093 };
11094
11095 // backwords compat
11096 Roo.Fx.resize = Roo.Fx.scale;
11097
11098 //When included, Roo.Fx is automatically applied to Element so that all basic
11099 //effects are available directly via the Element API
11100 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11101  * Based on:
11102  * Ext JS Library 1.1.1
11103  * Copyright(c) 2006-2007, Ext JS, LLC.
11104  *
11105  * Originally Released Under LGPL - original licence link has changed is not relivant.
11106  *
11107  * Fork - LGPL
11108  * <script type="text/javascript">
11109  */
11110
11111
11112 /**
11113  * @class Roo.CompositeElement
11114  * Standard composite class. Creates a Roo.Element for every element in the collection.
11115  * <br><br>
11116  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11117  * actions will be performed on all the elements in this collection.</b>
11118  * <br><br>
11119  * All methods return <i>this</i> and can be chained.
11120  <pre><code>
11121  var els = Roo.select("#some-el div.some-class", true);
11122  // or select directly from an existing element
11123  var el = Roo.get('some-el');
11124  el.select('div.some-class', true);
11125
11126  els.setWidth(100); // all elements become 100 width
11127  els.hide(true); // all elements fade out and hide
11128  // or
11129  els.setWidth(100).hide(true);
11130  </code></pre>
11131  */
11132 Roo.CompositeElement = function(els){
11133     this.elements = [];
11134     this.addElements(els);
11135 };
11136 Roo.CompositeElement.prototype = {
11137     isComposite: true,
11138     addElements : function(els){
11139         if(!els) {
11140             return this;
11141         }
11142         if(typeof els == "string"){
11143             els = Roo.Element.selectorFunction(els);
11144         }
11145         var yels = this.elements;
11146         var index = yels.length-1;
11147         for(var i = 0, len = els.length; i < len; i++) {
11148                 yels[++index] = Roo.get(els[i]);
11149         }
11150         return this;
11151     },
11152
11153     /**
11154     * Clears this composite and adds the elements returned by the passed selector.
11155     * @param {String/Array} els A string CSS selector, an array of elements or an element
11156     * @return {CompositeElement} this
11157     */
11158     fill : function(els){
11159         this.elements = [];
11160         this.add(els);
11161         return this;
11162     },
11163
11164     /**
11165     * Filters this composite to only elements that match the passed selector.
11166     * @param {String} selector A string CSS selector
11167     * @param {Boolean} inverse return inverse filter (not matches)
11168     * @return {CompositeElement} this
11169     */
11170     filter : function(selector, inverse){
11171         var els = [];
11172         inverse = inverse || false;
11173         this.each(function(el){
11174             var match = inverse ? !el.is(selector) : el.is(selector);
11175             if(match){
11176                 els[els.length] = el.dom;
11177             }
11178         });
11179         this.fill(els);
11180         return this;
11181     },
11182
11183     invoke : function(fn, args){
11184         var els = this.elements;
11185         for(var i = 0, len = els.length; i < len; i++) {
11186                 Roo.Element.prototype[fn].apply(els[i], args);
11187         }
11188         return this;
11189     },
11190     /**
11191     * Adds elements to this composite.
11192     * @param {String/Array} els A string CSS selector, an array of elements or an element
11193     * @return {CompositeElement} this
11194     */
11195     add : function(els){
11196         if(typeof els == "string"){
11197             this.addElements(Roo.Element.selectorFunction(els));
11198         }else if(els.length !== undefined){
11199             this.addElements(els);
11200         }else{
11201             this.addElements([els]);
11202         }
11203         return this;
11204     },
11205     /**
11206     * Calls the passed function passing (el, this, index) for each element in this composite.
11207     * @param {Function} fn The function to call
11208     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11209     * @return {CompositeElement} this
11210     */
11211     each : function(fn, scope){
11212         var els = this.elements;
11213         for(var i = 0, len = els.length; i < len; i++){
11214             if(fn.call(scope || els[i], els[i], this, i) === false) {
11215                 break;
11216             }
11217         }
11218         return this;
11219     },
11220
11221     /**
11222      * Returns the Element object at the specified index
11223      * @param {Number} index
11224      * @return {Roo.Element}
11225      */
11226     item : function(index){
11227         return this.elements[index] || null;
11228     },
11229
11230     /**
11231      * Returns the first Element
11232      * @return {Roo.Element}
11233      */
11234     first : function(){
11235         return this.item(0);
11236     },
11237
11238     /**
11239      * Returns the last Element
11240      * @return {Roo.Element}
11241      */
11242     last : function(){
11243         return this.item(this.elements.length-1);
11244     },
11245
11246     /**
11247      * Returns the number of elements in this composite
11248      * @return Number
11249      */
11250     getCount : function(){
11251         return this.elements.length;
11252     },
11253
11254     /**
11255      * Returns true if this composite contains the passed element
11256      * @return Boolean
11257      */
11258     contains : function(el){
11259         return this.indexOf(el) !== -1;
11260     },
11261
11262     /**
11263      * Returns true if this composite contains the passed element
11264      * @return Boolean
11265      */
11266     indexOf : function(el){
11267         return this.elements.indexOf(Roo.get(el));
11268     },
11269
11270
11271     /**
11272     * Removes the specified element(s).
11273     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11274     * or an array of any of those.
11275     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11276     * @return {CompositeElement} this
11277     */
11278     removeElement : function(el, removeDom){
11279         if(el instanceof Array){
11280             for(var i = 0, len = el.length; i < len; i++){
11281                 this.removeElement(el[i]);
11282             }
11283             return this;
11284         }
11285         var index = typeof el == 'number' ? el : this.indexOf(el);
11286         if(index !== -1){
11287             if(removeDom){
11288                 var d = this.elements[index];
11289                 if(d.dom){
11290                     d.remove();
11291                 }else{
11292                     d.parentNode.removeChild(d);
11293                 }
11294             }
11295             this.elements.splice(index, 1);
11296         }
11297         return this;
11298     },
11299
11300     /**
11301     * Replaces the specified element with the passed element.
11302     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11303     * to replace.
11304     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11305     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11306     * @return {CompositeElement} this
11307     */
11308     replaceElement : function(el, replacement, domReplace){
11309         var index = typeof el == 'number' ? el : this.indexOf(el);
11310         if(index !== -1){
11311             if(domReplace){
11312                 this.elements[index].replaceWith(replacement);
11313             }else{
11314                 this.elements.splice(index, 1, Roo.get(replacement))
11315             }
11316         }
11317         return this;
11318     },
11319
11320     /**
11321      * Removes all elements.
11322      */
11323     clear : function(){
11324         this.elements = [];
11325     }
11326 };
11327 (function(){
11328     Roo.CompositeElement.createCall = function(proto, fnName){
11329         if(!proto[fnName]){
11330             proto[fnName] = function(){
11331                 return this.invoke(fnName, arguments);
11332             };
11333         }
11334     };
11335     for(var fnName in Roo.Element.prototype){
11336         if(typeof Roo.Element.prototype[fnName] == "function"){
11337             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11338         }
11339     };
11340 })();
11341 /*
11342  * Based on:
11343  * Ext JS Library 1.1.1
11344  * Copyright(c) 2006-2007, Ext JS, LLC.
11345  *
11346  * Originally Released Under LGPL - original licence link has changed is not relivant.
11347  *
11348  * Fork - LGPL
11349  * <script type="text/javascript">
11350  */
11351
11352 /**
11353  * @class Roo.CompositeElementLite
11354  * @extends Roo.CompositeElement
11355  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11356  <pre><code>
11357  var els = Roo.select("#some-el div.some-class");
11358  // or select directly from an existing element
11359  var el = Roo.get('some-el');
11360  el.select('div.some-class');
11361
11362  els.setWidth(100); // all elements become 100 width
11363  els.hide(true); // all elements fade out and hide
11364  // or
11365  els.setWidth(100).hide(true);
11366  </code></pre><br><br>
11367  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11368  * actions will be performed on all the elements in this collection.</b>
11369  */
11370 Roo.CompositeElementLite = function(els){
11371     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11372     this.el = new Roo.Element.Flyweight();
11373 };
11374 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11375     addElements : function(els){
11376         if(els){
11377             if(els instanceof Array){
11378                 this.elements = this.elements.concat(els);
11379             }else{
11380                 var yels = this.elements;
11381                 var index = yels.length-1;
11382                 for(var i = 0, len = els.length; i < len; i++) {
11383                     yels[++index] = els[i];
11384                 }
11385             }
11386         }
11387         return this;
11388     },
11389     invoke : function(fn, args){
11390         var els = this.elements;
11391         var el = this.el;
11392         for(var i = 0, len = els.length; i < len; i++) {
11393             el.dom = els[i];
11394                 Roo.Element.prototype[fn].apply(el, args);
11395         }
11396         return this;
11397     },
11398     /**
11399      * Returns a flyweight Element of the dom element object at the specified index
11400      * @param {Number} index
11401      * @return {Roo.Element}
11402      */
11403     item : function(index){
11404         if(!this.elements[index]){
11405             return null;
11406         }
11407         this.el.dom = this.elements[index];
11408         return this.el;
11409     },
11410
11411     // fixes scope with flyweight
11412     addListener : function(eventName, handler, scope, opt){
11413         var els = this.elements;
11414         for(var i = 0, len = els.length; i < len; i++) {
11415             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11416         }
11417         return this;
11418     },
11419
11420     /**
11421     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11422     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11423     * a reference to the dom node, use el.dom.</b>
11424     * @param {Function} fn The function to call
11425     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11426     * @return {CompositeElement} this
11427     */
11428     each : function(fn, scope){
11429         var els = this.elements;
11430         var el = this.el;
11431         for(var i = 0, len = els.length; i < len; i++){
11432             el.dom = els[i];
11433                 if(fn.call(scope || el, el, this, i) === false){
11434                 break;
11435             }
11436         }
11437         return this;
11438     },
11439
11440     indexOf : function(el){
11441         return this.elements.indexOf(Roo.getDom(el));
11442     },
11443
11444     replaceElement : function(el, replacement, domReplace){
11445         var index = typeof el == 'number' ? el : this.indexOf(el);
11446         if(index !== -1){
11447             replacement = Roo.getDom(replacement);
11448             if(domReplace){
11449                 var d = this.elements[index];
11450                 d.parentNode.insertBefore(replacement, d);
11451                 d.parentNode.removeChild(d);
11452             }
11453             this.elements.splice(index, 1, replacement);
11454         }
11455         return this;
11456     }
11457 });
11458 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11459
11460 /*
11461  * Based on:
11462  * Ext JS Library 1.1.1
11463  * Copyright(c) 2006-2007, Ext JS, LLC.
11464  *
11465  * Originally Released Under LGPL - original licence link has changed is not relivant.
11466  *
11467  * Fork - LGPL
11468  * <script type="text/javascript">
11469  */
11470
11471  
11472
11473 /**
11474  * @class Roo.data.Connection
11475  * @extends Roo.util.Observable
11476  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11477  * either to a configured URL, or to a URL specified at request time. 
11478  * 
11479  * Requests made by this class are asynchronous, and will return immediately. No data from
11480  * the server will be available to the statement immediately following the {@link #request} call.
11481  * To process returned data, use a callback in the request options object, or an event listener.
11482  * 
11483  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11484  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11485  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11486  * property and, if present, the IFRAME's XML document as the responseXML property.
11487  * 
11488  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11489  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11490  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11491  * standard DOM methods.
11492  * @constructor
11493  * @param {Object} config a configuration object.
11494  */
11495 Roo.data.Connection = function(config){
11496     Roo.apply(this, config);
11497     this.addEvents({
11498         /**
11499          * @event beforerequest
11500          * Fires before a network request is made to retrieve a data object.
11501          * @param {Connection} conn This Connection object.
11502          * @param {Object} options The options config object passed to the {@link #request} method.
11503          */
11504         "beforerequest" : true,
11505         /**
11506          * @event requestcomplete
11507          * Fires if the request was successfully completed.
11508          * @param {Connection} conn This Connection object.
11509          * @param {Object} response The XHR object containing the response data.
11510          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11511          * @param {Object} options The options config object passed to the {@link #request} method.
11512          */
11513         "requestcomplete" : true,
11514         /**
11515          * @event requestexception
11516          * Fires if an error HTTP status was returned from the server.
11517          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11518          * @param {Connection} conn This Connection object.
11519          * @param {Object} response The XHR object containing the response data.
11520          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11521          * @param {Object} options The options config object passed to the {@link #request} method.
11522          */
11523         "requestexception" : true
11524     });
11525     Roo.data.Connection.superclass.constructor.call(this);
11526 };
11527
11528 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11529     /**
11530      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11531      */
11532     /**
11533      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11534      * extra parameters to each request made by this object. (defaults to undefined)
11535      */
11536     /**
11537      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11538      *  to each request made by this object. (defaults to undefined)
11539      */
11540     /**
11541      * @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)
11542      */
11543     /**
11544      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11545      */
11546     timeout : 30000,
11547     /**
11548      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11549      * @type Boolean
11550      */
11551     autoAbort:false,
11552
11553     /**
11554      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11555      * @type Boolean
11556      */
11557     disableCaching: true,
11558
11559     /**
11560      * Sends an HTTP request to a remote server.
11561      * @param {Object} options An object which may contain the following properties:<ul>
11562      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11563      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11564      * request, a url encoded string or a function to call to get either.</li>
11565      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11566      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11567      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11568      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11569      * <li>options {Object} The parameter to the request call.</li>
11570      * <li>success {Boolean} True if the request succeeded.</li>
11571      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11572      * </ul></li>
11573      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11574      * The callback is passed the following parameters:<ul>
11575      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11576      * <li>options {Object} The parameter to the request call.</li>
11577      * </ul></li>
11578      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11579      * The callback is passed the following parameters:<ul>
11580      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11581      * <li>options {Object} The parameter to the request call.</li>
11582      * </ul></li>
11583      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11584      * for the callback function. Defaults to the browser window.</li>
11585      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11586      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11587      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11588      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11589      * params for the post data. Any params will be appended to the URL.</li>
11590      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11591      * </ul>
11592      * @return {Number} transactionId
11593      */
11594     request : function(o){
11595         if(this.fireEvent("beforerequest", this, o) !== false){
11596             var p = o.params;
11597
11598             if(typeof p == "function"){
11599                 p = p.call(o.scope||window, o);
11600             }
11601             if(typeof p == "object"){
11602                 p = Roo.urlEncode(o.params);
11603             }
11604             if(this.extraParams){
11605                 var extras = Roo.urlEncode(this.extraParams);
11606                 p = p ? (p + '&' + extras) : extras;
11607             }
11608
11609             var url = o.url || this.url;
11610             if(typeof url == 'function'){
11611                 url = url.call(o.scope||window, o);
11612             }
11613
11614             if(o.form){
11615                 var form = Roo.getDom(o.form);
11616                 url = url || form.action;
11617
11618                 var enctype = form.getAttribute("enctype");
11619                 
11620                 if (o.formData) {
11621                     return this.doFormDataUpload(o,p,url);
11622                 }
11623                 
11624                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11625                     return this.doFormUpload(o, p, url);
11626                 }
11627                 var f = Roo.lib.Ajax.serializeForm(form);
11628                 p = p ? (p + '&' + f) : f;
11629             }
11630
11631             var hs = o.headers;
11632             if(this.defaultHeaders){
11633                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11634                 if(!o.headers){
11635                     o.headers = hs;
11636                 }
11637             }
11638
11639             var cb = {
11640                 success: this.handleResponse,
11641                 failure: this.handleFailure,
11642                 scope: this,
11643                 argument: {options: o},
11644                 timeout : o.timeout || this.timeout
11645             };
11646
11647             var method = o.method||this.method||(p ? "POST" : "GET");
11648
11649             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11650                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11651             }
11652
11653             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11654                 if(o.autoAbort){
11655                     this.abort();
11656                 }
11657             }else if(this.autoAbort !== false){
11658                 this.abort();
11659             }
11660
11661             if((method == 'GET' && p) || o.xmlData){
11662                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11663                 p = '';
11664             }
11665             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11666             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11667             Roo.lib.Ajax.useDefaultHeader == true;
11668             return this.transId;
11669         }else{
11670             Roo.callback(o.callback, o.scope, [o, null, null]);
11671             return null;
11672         }
11673     },
11674
11675     /**
11676      * Determine whether this object has a request outstanding.
11677      * @param {Number} transactionId (Optional) defaults to the last transaction
11678      * @return {Boolean} True if there is an outstanding request.
11679      */
11680     isLoading : function(transId){
11681         if(transId){
11682             return Roo.lib.Ajax.isCallInProgress(transId);
11683         }else{
11684             return this.transId ? true : false;
11685         }
11686     },
11687
11688     /**
11689      * Aborts any outstanding request.
11690      * @param {Number} transactionId (Optional) defaults to the last transaction
11691      */
11692     abort : function(transId){
11693         if(transId || this.isLoading()){
11694             Roo.lib.Ajax.abort(transId || this.transId);
11695         }
11696     },
11697
11698     // private
11699     handleResponse : function(response){
11700         this.transId = false;
11701         var options = response.argument.options;
11702         response.argument = options ? options.argument : null;
11703         this.fireEvent("requestcomplete", this, response, options);
11704         Roo.callback(options.success, options.scope, [response, options]);
11705         Roo.callback(options.callback, options.scope, [options, true, response]);
11706     },
11707
11708     // private
11709     handleFailure : function(response, e){
11710         this.transId = false;
11711         var options = response.argument.options;
11712         response.argument = options ? options.argument : null;
11713         this.fireEvent("requestexception", this, response, options, e);
11714         Roo.callback(options.failure, options.scope, [response, options]);
11715         Roo.callback(options.callback, options.scope, [options, false, response]);
11716     },
11717
11718     // private
11719     doFormUpload : function(o, ps, url){
11720         var id = Roo.id();
11721         var frame = document.createElement('iframe');
11722         frame.id = id;
11723         frame.name = id;
11724         frame.className = 'x-hidden';
11725         if(Roo.isIE){
11726             frame.src = Roo.SSL_SECURE_URL;
11727         }
11728         document.body.appendChild(frame);
11729
11730         if(Roo.isIE){
11731            document.frames[id].name = id;
11732         }
11733
11734         var form = Roo.getDom(o.form);
11735         form.target = id;
11736         form.method = 'POST';
11737         form.enctype = form.encoding = 'multipart/form-data';
11738         if(url){
11739             form.action = url;
11740         }
11741
11742         var hiddens, hd;
11743         if(ps){ // add dynamic params
11744             hiddens = [];
11745             ps = Roo.urlDecode(ps, false);
11746             for(var k in ps){
11747                 if(ps.hasOwnProperty(k)){
11748                     hd = document.createElement('input');
11749                     hd.type = 'hidden';
11750                     hd.name = k;
11751                     hd.value = ps[k];
11752                     form.appendChild(hd);
11753                     hiddens.push(hd);
11754                 }
11755             }
11756         }
11757
11758         function cb(){
11759             var r = {  // bogus response object
11760                 responseText : '',
11761                 responseXML : null
11762             };
11763
11764             r.argument = o ? o.argument : null;
11765
11766             try { //
11767                 var doc;
11768                 if(Roo.isIE){
11769                     doc = frame.contentWindow.document;
11770                 }else {
11771                     doc = (frame.contentDocument || window.frames[id].document);
11772                 }
11773                 if(doc && doc.body){
11774                     r.responseText = doc.body.innerHTML;
11775                 }
11776                 if(doc && doc.XMLDocument){
11777                     r.responseXML = doc.XMLDocument;
11778                 }else {
11779                     r.responseXML = doc;
11780                 }
11781             }
11782             catch(e) {
11783                 // ignore
11784             }
11785
11786             Roo.EventManager.removeListener(frame, 'load', cb, this);
11787
11788             this.fireEvent("requestcomplete", this, r, o);
11789             Roo.callback(o.success, o.scope, [r, o]);
11790             Roo.callback(o.callback, o.scope, [o, true, r]);
11791
11792             setTimeout(function(){document.body.removeChild(frame);}, 100);
11793         }
11794
11795         Roo.EventManager.on(frame, 'load', cb, this);
11796         form.submit();
11797
11798         if(hiddens){ // remove dynamic params
11799             for(var i = 0, len = hiddens.length; i < len; i++){
11800                 form.removeChild(hiddens[i]);
11801             }
11802         }
11803     },
11804     // this is a 'formdata version???'
11805     
11806     
11807     doFormDataUpload : function(o, ps, url)
11808     {
11809         var form = Roo.getDom(o.form);
11810         form.enctype = form.encoding = 'multipart/form-data';
11811         var formData = o.formData === true ? new FormData(form) : o.formData;
11812       
11813         var cb = {
11814             success: this.handleResponse,
11815             failure: this.handleFailure,
11816             scope: this,
11817             argument: {options: o},
11818             timeout : o.timeout || this.timeout
11819         };
11820  
11821         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11822             if(o.autoAbort){
11823                 this.abort();
11824             }
11825         }else if(this.autoAbort !== false){
11826             this.abort();
11827         }
11828
11829         //Roo.lib.Ajax.defaultPostHeader = null;
11830         Roo.lib.Ajax.useDefaultHeader = false;
11831         this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
11832         Roo.lib.Ajax.useDefaultHeader = true;
11833  
11834          
11835     }
11836     
11837 });
11838 /*
11839  * Based on:
11840  * Ext JS Library 1.1.1
11841  * Copyright(c) 2006-2007, Ext JS, LLC.
11842  *
11843  * Originally Released Under LGPL - original licence link has changed is not relivant.
11844  *
11845  * Fork - LGPL
11846  * <script type="text/javascript">
11847  */
11848  
11849 /**
11850  * Global Ajax request class.
11851  * 
11852  * @class Roo.Ajax
11853  * @extends Roo.data.Connection
11854  * @static
11855  * 
11856  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11857  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11858  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11859  * @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)
11860  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11861  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11862  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11863  */
11864 Roo.Ajax = new Roo.data.Connection({
11865     // fix up the docs
11866     /**
11867      * @scope Roo.Ajax
11868      * @type {Boolear} 
11869      */
11870     autoAbort : false,
11871
11872     /**
11873      * Serialize the passed form into a url encoded string
11874      * @scope Roo.Ajax
11875      * @param {String/HTMLElement} form
11876      * @return {String}
11877      */
11878     serializeForm : function(form){
11879         return Roo.lib.Ajax.serializeForm(form);
11880     }
11881 });/*
11882  * Based on:
11883  * Ext JS Library 1.1.1
11884  * Copyright(c) 2006-2007, Ext JS, LLC.
11885  *
11886  * Originally Released Under LGPL - original licence link has changed is not relivant.
11887  *
11888  * Fork - LGPL
11889  * <script type="text/javascript">
11890  */
11891
11892  
11893 /**
11894  * @class Roo.UpdateManager
11895  * @extends Roo.util.Observable
11896  * Provides AJAX-style update for Element object.<br><br>
11897  * Usage:<br>
11898  * <pre><code>
11899  * // Get it from a Roo.Element object
11900  * var el = Roo.get("foo");
11901  * var mgr = el.getUpdateManager();
11902  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11903  * ...
11904  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11905  * <br>
11906  * // or directly (returns the same UpdateManager instance)
11907  * var mgr = new Roo.UpdateManager("myElementId");
11908  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11909  * mgr.on("update", myFcnNeedsToKnow);
11910  * <br>
11911    // short handed call directly from the element object
11912    Roo.get("foo").load({
11913         url: "bar.php",
11914         scripts:true,
11915         params: "for=bar",
11916         text: "Loading Foo..."
11917    });
11918  * </code></pre>
11919  * @constructor
11920  * Create new UpdateManager directly.
11921  * @param {String/HTMLElement/Roo.Element} el The element to update
11922  * @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).
11923  */
11924 Roo.UpdateManager = function(el, forceNew){
11925     el = Roo.get(el);
11926     if(!forceNew && el.updateManager){
11927         return el.updateManager;
11928     }
11929     /**
11930      * The Element object
11931      * @type Roo.Element
11932      */
11933     this.el = el;
11934     /**
11935      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11936      * @type String
11937      */
11938     this.defaultUrl = null;
11939
11940     this.addEvents({
11941         /**
11942          * @event beforeupdate
11943          * Fired before an update is made, return false from your handler and the update is cancelled.
11944          * @param {Roo.Element} el
11945          * @param {String/Object/Function} url
11946          * @param {String/Object} params
11947          */
11948         "beforeupdate": true,
11949         /**
11950          * @event update
11951          * Fired after successful update is made.
11952          * @param {Roo.Element} el
11953          * @param {Object} oResponseObject The response Object
11954          */
11955         "update": true,
11956         /**
11957          * @event failure
11958          * Fired on update failure.
11959          * @param {Roo.Element} el
11960          * @param {Object} oResponseObject The response Object
11961          */
11962         "failure": true
11963     });
11964     var d = Roo.UpdateManager.defaults;
11965     /**
11966      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11967      * @type String
11968      */
11969     this.sslBlankUrl = d.sslBlankUrl;
11970     /**
11971      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11972      * @type Boolean
11973      */
11974     this.disableCaching = d.disableCaching;
11975     /**
11976      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11977      * @type String
11978      */
11979     this.indicatorText = d.indicatorText;
11980     /**
11981      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11982      * @type String
11983      */
11984     this.showLoadIndicator = d.showLoadIndicator;
11985     /**
11986      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11987      * @type Number
11988      */
11989     this.timeout = d.timeout;
11990
11991     /**
11992      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11993      * @type Boolean
11994      */
11995     this.loadScripts = d.loadScripts;
11996
11997     /**
11998      * Transaction object of current executing transaction
11999      */
12000     this.transaction = null;
12001
12002     /**
12003      * @private
12004      */
12005     this.autoRefreshProcId = null;
12006     /**
12007      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12008      * @type Function
12009      */
12010     this.refreshDelegate = this.refresh.createDelegate(this);
12011     /**
12012      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12013      * @type Function
12014      */
12015     this.updateDelegate = this.update.createDelegate(this);
12016     /**
12017      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12018      * @type Function
12019      */
12020     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12021     /**
12022      * @private
12023      */
12024     this.successDelegate = this.processSuccess.createDelegate(this);
12025     /**
12026      * @private
12027      */
12028     this.failureDelegate = this.processFailure.createDelegate(this);
12029
12030     if(!this.renderer){
12031      /**
12032       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12033       */
12034     this.renderer = new Roo.UpdateManager.BasicRenderer();
12035     }
12036     
12037     Roo.UpdateManager.superclass.constructor.call(this);
12038 };
12039
12040 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12041     /**
12042      * Get the Element this UpdateManager is bound to
12043      * @return {Roo.Element} The element
12044      */
12045     getEl : function(){
12046         return this.el;
12047     },
12048     /**
12049      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12050      * @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:
12051 <pre><code>
12052 um.update({<br/>
12053     url: "your-url.php",<br/>
12054     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12055     callback: yourFunction,<br/>
12056     scope: yourObject, //(optional scope)  <br/>
12057     discardUrl: false, <br/>
12058     nocache: false,<br/>
12059     text: "Loading...",<br/>
12060     timeout: 30,<br/>
12061     scripts: false<br/>
12062 });
12063 </code></pre>
12064      * The only required property is url. The optional properties nocache, text and scripts
12065      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12066      * @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}
12067      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12068      * @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.
12069      */
12070     update : function(url, params, callback, discardUrl){
12071         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12072             var method = this.method,
12073                 cfg;
12074             if(typeof url == "object"){ // must be config object
12075                 cfg = url;
12076                 url = cfg.url;
12077                 params = params || cfg.params;
12078                 callback = callback || cfg.callback;
12079                 discardUrl = discardUrl || cfg.discardUrl;
12080                 if(callback && cfg.scope){
12081                     callback = callback.createDelegate(cfg.scope);
12082                 }
12083                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12084                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12085                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12086                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12087                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12088             }
12089             this.showLoading();
12090             if(!discardUrl){
12091                 this.defaultUrl = url;
12092             }
12093             if(typeof url == "function"){
12094                 url = url.call(this);
12095             }
12096
12097             method = method || (params ? "POST" : "GET");
12098             if(method == "GET"){
12099                 url = this.prepareUrl(url);
12100             }
12101
12102             var o = Roo.apply(cfg ||{}, {
12103                 url : url,
12104                 params: params,
12105                 success: this.successDelegate,
12106                 failure: this.failureDelegate,
12107                 callback: undefined,
12108                 timeout: (this.timeout*1000),
12109                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12110             });
12111             Roo.log("updated manager called with timeout of " + o.timeout);
12112             this.transaction = Roo.Ajax.request(o);
12113         }
12114     },
12115
12116     /**
12117      * 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.
12118      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12119      * @param {String/HTMLElement} form The form Id or form element
12120      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12121      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12122      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12123      */
12124     formUpdate : function(form, url, reset, callback){
12125         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12126             if(typeof url == "function"){
12127                 url = url.call(this);
12128             }
12129             form = Roo.getDom(form);
12130             this.transaction = Roo.Ajax.request({
12131                 form: form,
12132                 url:url,
12133                 success: this.successDelegate,
12134                 failure: this.failureDelegate,
12135                 timeout: (this.timeout*1000),
12136                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12137             });
12138             this.showLoading.defer(1, this);
12139         }
12140     },
12141
12142     /**
12143      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12144      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12145      */
12146     refresh : function(callback){
12147         if(this.defaultUrl == null){
12148             return;
12149         }
12150         this.update(this.defaultUrl, null, callback, true);
12151     },
12152
12153     /**
12154      * Set this element to auto refresh.
12155      * @param {Number} interval How often to update (in seconds).
12156      * @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)
12157      * @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}
12158      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12159      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12160      */
12161     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12162         if(refreshNow){
12163             this.update(url || this.defaultUrl, params, callback, true);
12164         }
12165         if(this.autoRefreshProcId){
12166             clearInterval(this.autoRefreshProcId);
12167         }
12168         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12169     },
12170
12171     /**
12172      * Stop auto refresh on this element.
12173      */
12174      stopAutoRefresh : function(){
12175         if(this.autoRefreshProcId){
12176             clearInterval(this.autoRefreshProcId);
12177             delete this.autoRefreshProcId;
12178         }
12179     },
12180
12181     isAutoRefreshing : function(){
12182        return this.autoRefreshProcId ? true : false;
12183     },
12184     /**
12185      * Called to update the element to "Loading" state. Override to perform custom action.
12186      */
12187     showLoading : function(){
12188         if(this.showLoadIndicator){
12189             this.el.update(this.indicatorText);
12190         }
12191     },
12192
12193     /**
12194      * Adds unique parameter to query string if disableCaching = true
12195      * @private
12196      */
12197     prepareUrl : function(url){
12198         if(this.disableCaching){
12199             var append = "_dc=" + (new Date().getTime());
12200             if(url.indexOf("?") !== -1){
12201                 url += "&" + append;
12202             }else{
12203                 url += "?" + append;
12204             }
12205         }
12206         return url;
12207     },
12208
12209     /**
12210      * @private
12211      */
12212     processSuccess : function(response){
12213         this.transaction = null;
12214         if(response.argument.form && response.argument.reset){
12215             try{ // put in try/catch since some older FF releases had problems with this
12216                 response.argument.form.reset();
12217             }catch(e){}
12218         }
12219         if(this.loadScripts){
12220             this.renderer.render(this.el, response, this,
12221                 this.updateComplete.createDelegate(this, [response]));
12222         }else{
12223             this.renderer.render(this.el, response, this);
12224             this.updateComplete(response);
12225         }
12226     },
12227
12228     updateComplete : function(response){
12229         this.fireEvent("update", this.el, response);
12230         if(typeof response.argument.callback == "function"){
12231             response.argument.callback(this.el, true, response);
12232         }
12233     },
12234
12235     /**
12236      * @private
12237      */
12238     processFailure : function(response){
12239         this.transaction = null;
12240         this.fireEvent("failure", this.el, response);
12241         if(typeof response.argument.callback == "function"){
12242             response.argument.callback(this.el, false, response);
12243         }
12244     },
12245
12246     /**
12247      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12248      * @param {Object} renderer The object implementing the render() method
12249      */
12250     setRenderer : function(renderer){
12251         this.renderer = renderer;
12252     },
12253
12254     getRenderer : function(){
12255        return this.renderer;
12256     },
12257
12258     /**
12259      * Set the defaultUrl used for updates
12260      * @param {String/Function} defaultUrl The url or a function to call to get the url
12261      */
12262     setDefaultUrl : function(defaultUrl){
12263         this.defaultUrl = defaultUrl;
12264     },
12265
12266     /**
12267      * Aborts the executing transaction
12268      */
12269     abort : function(){
12270         if(this.transaction){
12271             Roo.Ajax.abort(this.transaction);
12272         }
12273     },
12274
12275     /**
12276      * Returns true if an update is in progress
12277      * @return {Boolean}
12278      */
12279     isUpdating : function(){
12280         if(this.transaction){
12281             return Roo.Ajax.isLoading(this.transaction);
12282         }
12283         return false;
12284     }
12285 });
12286
12287 /**
12288  * @class Roo.UpdateManager.defaults
12289  * @static (not really - but it helps the doc tool)
12290  * The defaults collection enables customizing the default properties of UpdateManager
12291  */
12292    Roo.UpdateManager.defaults = {
12293        /**
12294          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12295          * @type Number
12296          */
12297          timeout : 30,
12298
12299          /**
12300          * True to process scripts by default (Defaults to false).
12301          * @type Boolean
12302          */
12303         loadScripts : false,
12304
12305         /**
12306         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12307         * @type String
12308         */
12309         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12310         /**
12311          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12312          * @type Boolean
12313          */
12314         disableCaching : false,
12315         /**
12316          * Whether to show indicatorText when loading (Defaults to true).
12317          * @type Boolean
12318          */
12319         showLoadIndicator : true,
12320         /**
12321          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12322          * @type String
12323          */
12324         indicatorText : '<div class="loading-indicator">Loading...</div>'
12325    };
12326
12327 /**
12328  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12329  *Usage:
12330  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12331  * @param {String/HTMLElement/Roo.Element} el The element to update
12332  * @param {String} url The url
12333  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12334  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12335  * @static
12336  * @deprecated
12337  * @member Roo.UpdateManager
12338  */
12339 Roo.UpdateManager.updateElement = function(el, url, params, options){
12340     var um = Roo.get(el, true).getUpdateManager();
12341     Roo.apply(um, options);
12342     um.update(url, params, options ? options.callback : null);
12343 };
12344 // alias for backwards compat
12345 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12346 /**
12347  * @class Roo.UpdateManager.BasicRenderer
12348  * Default Content renderer. Updates the elements innerHTML with the responseText.
12349  */
12350 Roo.UpdateManager.BasicRenderer = function(){};
12351
12352 Roo.UpdateManager.BasicRenderer.prototype = {
12353     /**
12354      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12355      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12356      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12357      * @param {Roo.Element} el The element being rendered
12358      * @param {Object} response The YUI Connect response object
12359      * @param {UpdateManager} updateManager The calling update manager
12360      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12361      */
12362      render : function(el, response, updateManager, callback){
12363         el.update(response.responseText, updateManager.loadScripts, callback);
12364     }
12365 };
12366 /*
12367  * Based on:
12368  * Roo JS
12369  * (c)) Alan Knowles
12370  * Licence : LGPL
12371  */
12372
12373
12374 /**
12375  * @class Roo.DomTemplate
12376  * @extends Roo.Template
12377  * An effort at a dom based template engine..
12378  *
12379  * Similar to XTemplate, except it uses dom parsing to create the template..
12380  *
12381  * Supported features:
12382  *
12383  *  Tags:
12384
12385 <pre><code>
12386       {a_variable} - output encoded.
12387       {a_variable.format:("Y-m-d")} - call a method on the variable
12388       {a_variable:raw} - unencoded output
12389       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12390       {a_variable:this.method_on_template(...)} - call a method on the template object.
12391  
12392 </code></pre>
12393  *  The tpl tag:
12394 <pre><code>
12395         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12396         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12397         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12398         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12399   
12400 </code></pre>
12401  *      
12402  */
12403 Roo.DomTemplate = function()
12404 {
12405      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12406      if (this.html) {
12407         this.compile();
12408      }
12409 };
12410
12411
12412 Roo.extend(Roo.DomTemplate, Roo.Template, {
12413     /**
12414      * id counter for sub templates.
12415      */
12416     id : 0,
12417     /**
12418      * flag to indicate if dom parser is inside a pre,
12419      * it will strip whitespace if not.
12420      */
12421     inPre : false,
12422     
12423     /**
12424      * The various sub templates
12425      */
12426     tpls : false,
12427     
12428     
12429     
12430     /**
12431      *
12432      * basic tag replacing syntax
12433      * WORD:WORD()
12434      *
12435      * // you can fake an object call by doing this
12436      *  x.t:(test,tesT) 
12437      * 
12438      */
12439     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12440     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12441     
12442     iterChild : function (node, method) {
12443         
12444         var oldPre = this.inPre;
12445         if (node.tagName == 'PRE') {
12446             this.inPre = true;
12447         }
12448         for( var i = 0; i < node.childNodes.length; i++) {
12449             method.call(this, node.childNodes[i]);
12450         }
12451         this.inPre = oldPre;
12452     },
12453     
12454     
12455     
12456     /**
12457      * compile the template
12458      *
12459      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12460      *
12461      */
12462     compile: function()
12463     {
12464         var s = this.html;
12465         
12466         // covert the html into DOM...
12467         var doc = false;
12468         var div =false;
12469         try {
12470             doc = document.implementation.createHTMLDocument("");
12471             doc.documentElement.innerHTML =   this.html  ;
12472             div = doc.documentElement;
12473         } catch (e) {
12474             // old IE... - nasty -- it causes all sorts of issues.. with
12475             // images getting pulled from server..
12476             div = document.createElement('div');
12477             div.innerHTML = this.html;
12478         }
12479         //doc.documentElement.innerHTML = htmlBody
12480          
12481         
12482         
12483         this.tpls = [];
12484         var _t = this;
12485         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12486         
12487         var tpls = this.tpls;
12488         
12489         // create a top level template from the snippet..
12490         
12491         //Roo.log(div.innerHTML);
12492         
12493         var tpl = {
12494             uid : 'master',
12495             id : this.id++,
12496             attr : false,
12497             value : false,
12498             body : div.innerHTML,
12499             
12500             forCall : false,
12501             execCall : false,
12502             dom : div,
12503             isTop : true
12504             
12505         };
12506         tpls.unshift(tpl);
12507         
12508         
12509         // compile them...
12510         this.tpls = [];
12511         Roo.each(tpls, function(tp){
12512             this.compileTpl(tp);
12513             this.tpls[tp.id] = tp;
12514         }, this);
12515         
12516         this.master = tpls[0];
12517         return this;
12518         
12519         
12520     },
12521     
12522     compileNode : function(node, istop) {
12523         // test for
12524         //Roo.log(node);
12525         
12526         
12527         // skip anything not a tag..
12528         if (node.nodeType != 1) {
12529             if (node.nodeType == 3 && !this.inPre) {
12530                 // reduce white space..
12531                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12532                 
12533             }
12534             return;
12535         }
12536         
12537         var tpl = {
12538             uid : false,
12539             id : false,
12540             attr : false,
12541             value : false,
12542             body : '',
12543             
12544             forCall : false,
12545             execCall : false,
12546             dom : false,
12547             isTop : istop
12548             
12549             
12550         };
12551         
12552         
12553         switch(true) {
12554             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12555             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12556             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12557             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12558             // no default..
12559         }
12560         
12561         
12562         if (!tpl.attr) {
12563             // just itterate children..
12564             this.iterChild(node,this.compileNode);
12565             return;
12566         }
12567         tpl.uid = this.id++;
12568         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12569         node.removeAttribute('roo-'+ tpl.attr);
12570         if (tpl.attr != 'name') {
12571             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12572             node.parentNode.replaceChild(placeholder,  node);
12573         } else {
12574             
12575             var placeholder =  document.createElement('span');
12576             placeholder.className = 'roo-tpl-' + tpl.value;
12577             node.parentNode.replaceChild(placeholder,  node);
12578         }
12579         
12580         // parent now sees '{domtplXXXX}
12581         this.iterChild(node,this.compileNode);
12582         
12583         // we should now have node body...
12584         var div = document.createElement('div');
12585         div.appendChild(node);
12586         tpl.dom = node;
12587         // this has the unfortunate side effect of converting tagged attributes
12588         // eg. href="{...}" into %7C...%7D
12589         // this has been fixed by searching for those combo's although it's a bit hacky..
12590         
12591         
12592         tpl.body = div.innerHTML;
12593         
12594         
12595          
12596         tpl.id = tpl.uid;
12597         switch(tpl.attr) {
12598             case 'for' :
12599                 switch (tpl.value) {
12600                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12601                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12602                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12603                 }
12604                 break;
12605             
12606             case 'exec':
12607                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12608                 break;
12609             
12610             case 'if':     
12611                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12612                 break;
12613             
12614             case 'name':
12615                 tpl.id  = tpl.value; // replace non characters???
12616                 break;
12617             
12618         }
12619         
12620         
12621         this.tpls.push(tpl);
12622         
12623         
12624         
12625     },
12626     
12627     
12628     
12629     
12630     /**
12631      * Compile a segment of the template into a 'sub-template'
12632      *
12633      * 
12634      * 
12635      *
12636      */
12637     compileTpl : function(tpl)
12638     {
12639         var fm = Roo.util.Format;
12640         var useF = this.disableFormats !== true;
12641         
12642         var sep = Roo.isGecko ? "+\n" : ",\n";
12643         
12644         var undef = function(str) {
12645             Roo.debug && Roo.log("Property not found :"  + str);
12646             return '';
12647         };
12648           
12649         //Roo.log(tpl.body);
12650         
12651         
12652         
12653         var fn = function(m, lbrace, name, format, args)
12654         {
12655             //Roo.log("ARGS");
12656             //Roo.log(arguments);
12657             args = args ? args.replace(/\\'/g,"'") : args;
12658             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12659             if (typeof(format) == 'undefined') {
12660                 format =  'htmlEncode'; 
12661             }
12662             if (format == 'raw' ) {
12663                 format = false;
12664             }
12665             
12666             if(name.substr(0, 6) == 'domtpl'){
12667                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12668             }
12669             
12670             // build an array of options to determine if value is undefined..
12671             
12672             // basically get 'xxxx.yyyy' then do
12673             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12674             //    (function () { Roo.log("Property not found"); return ''; })() :
12675             //    ......
12676             
12677             var udef_ar = [];
12678             var lookfor = '';
12679             Roo.each(name.split('.'), function(st) {
12680                 lookfor += (lookfor.length ? '.': '') + st;
12681                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12682             });
12683             
12684             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12685             
12686             
12687             if(format && useF){
12688                 
12689                 args = args ? ',' + args : "";
12690                  
12691                 if(format.substr(0, 5) != "this."){
12692                     format = "fm." + format + '(';
12693                 }else{
12694                     format = 'this.call("'+ format.substr(5) + '", ';
12695                     args = ", values";
12696                 }
12697                 
12698                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12699             }
12700              
12701             if (args && args.length) {
12702                 // called with xxyx.yuu:(test,test)
12703                 // change to ()
12704                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12705             }
12706             // raw.. - :raw modifier..
12707             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12708             
12709         };
12710         var body;
12711         // branched to use + in gecko and [].join() in others
12712         if(Roo.isGecko){
12713             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12714                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12715                     "';};};";
12716         }else{
12717             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12718             body.push(tpl.body.replace(/(\r\n|\n)/g,
12719                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12720             body.push("'].join('');};};");
12721             body = body.join('');
12722         }
12723         
12724         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12725        
12726         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12727         eval(body);
12728         
12729         return this;
12730     },
12731      
12732     /**
12733      * same as applyTemplate, except it's done to one of the subTemplates
12734      * when using named templates, you can do:
12735      *
12736      * var str = pl.applySubTemplate('your-name', values);
12737      *
12738      * 
12739      * @param {Number} id of the template
12740      * @param {Object} values to apply to template
12741      * @param {Object} parent (normaly the instance of this object)
12742      */
12743     applySubTemplate : function(id, values, parent)
12744     {
12745         
12746         
12747         var t = this.tpls[id];
12748         
12749         
12750         try { 
12751             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12752                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12753                 return '';
12754             }
12755         } catch(e) {
12756             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12757             Roo.log(values);
12758           
12759             return '';
12760         }
12761         try { 
12762             
12763             if(t.execCall && t.execCall.call(this, values, parent)){
12764                 return '';
12765             }
12766         } catch(e) {
12767             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12768             Roo.log(values);
12769             return '';
12770         }
12771         
12772         try {
12773             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12774             parent = t.target ? values : parent;
12775             if(t.forCall && vs instanceof Array){
12776                 var buf = [];
12777                 for(var i = 0, len = vs.length; i < len; i++){
12778                     try {
12779                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12780                     } catch (e) {
12781                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12782                         Roo.log(e.body);
12783                         //Roo.log(t.compiled);
12784                         Roo.log(vs[i]);
12785                     }   
12786                 }
12787                 return buf.join('');
12788             }
12789         } catch (e) {
12790             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12791             Roo.log(values);
12792             return '';
12793         }
12794         try {
12795             return t.compiled.call(this, vs, parent);
12796         } catch (e) {
12797             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12798             Roo.log(e.body);
12799             //Roo.log(t.compiled);
12800             Roo.log(values);
12801             return '';
12802         }
12803     },
12804
12805    
12806
12807     applyTemplate : function(values){
12808         return this.master.compiled.call(this, values, {});
12809         //var s = this.subs;
12810     },
12811
12812     apply : function(){
12813         return this.applyTemplate.apply(this, arguments);
12814     }
12815
12816  });
12817
12818 Roo.DomTemplate.from = function(el){
12819     el = Roo.getDom(el);
12820     return new Roo.Domtemplate(el.value || el.innerHTML);
12821 };/*
12822  * Based on:
12823  * Ext JS Library 1.1.1
12824  * Copyright(c) 2006-2007, Ext JS, LLC.
12825  *
12826  * Originally Released Under LGPL - original licence link has changed is not relivant.
12827  *
12828  * Fork - LGPL
12829  * <script type="text/javascript">
12830  */
12831
12832 /**
12833  * @class Roo.util.DelayedTask
12834  * Provides a convenient method of performing setTimeout where a new
12835  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12836  * You can use this class to buffer
12837  * the keypress events for a certain number of milliseconds, and perform only if they stop
12838  * for that amount of time.
12839  * @constructor The parameters to this constructor serve as defaults and are not required.
12840  * @param {Function} fn (optional) The default function to timeout
12841  * @param {Object} scope (optional) The default scope of that timeout
12842  * @param {Array} args (optional) The default Array of arguments
12843  */
12844 Roo.util.DelayedTask = function(fn, scope, args){
12845     var id = null, d, t;
12846
12847     var call = function(){
12848         var now = new Date().getTime();
12849         if(now - t >= d){
12850             clearInterval(id);
12851             id = null;
12852             fn.apply(scope, args || []);
12853         }
12854     };
12855     /**
12856      * Cancels any pending timeout and queues a new one
12857      * @param {Number} delay The milliseconds to delay
12858      * @param {Function} newFn (optional) Overrides function passed to constructor
12859      * @param {Object} newScope (optional) Overrides scope passed to constructor
12860      * @param {Array} newArgs (optional) Overrides args passed to constructor
12861      */
12862     this.delay = function(delay, newFn, newScope, newArgs){
12863         if(id && delay != d){
12864             this.cancel();
12865         }
12866         d = delay;
12867         t = new Date().getTime();
12868         fn = newFn || fn;
12869         scope = newScope || scope;
12870         args = newArgs || args;
12871         if(!id){
12872             id = setInterval(call, d);
12873         }
12874     };
12875
12876     /**
12877      * Cancel the last queued timeout
12878      */
12879     this.cancel = function(){
12880         if(id){
12881             clearInterval(id);
12882             id = null;
12883         }
12884     };
12885 };/*
12886  * Based on:
12887  * Ext JS Library 1.1.1
12888  * Copyright(c) 2006-2007, Ext JS, LLC.
12889  *
12890  * Originally Released Under LGPL - original licence link has changed is not relivant.
12891  *
12892  * Fork - LGPL
12893  * <script type="text/javascript">
12894  */
12895  
12896  
12897 Roo.util.TaskRunner = function(interval){
12898     interval = interval || 10;
12899     var tasks = [], removeQueue = [];
12900     var id = 0;
12901     var running = false;
12902
12903     var stopThread = function(){
12904         running = false;
12905         clearInterval(id);
12906         id = 0;
12907     };
12908
12909     var startThread = function(){
12910         if(!running){
12911             running = true;
12912             id = setInterval(runTasks, interval);
12913         }
12914     };
12915
12916     var removeTask = function(task){
12917         removeQueue.push(task);
12918         if(task.onStop){
12919             task.onStop();
12920         }
12921     };
12922
12923     var runTasks = function(){
12924         if(removeQueue.length > 0){
12925             for(var i = 0, len = removeQueue.length; i < len; i++){
12926                 tasks.remove(removeQueue[i]);
12927             }
12928             removeQueue = [];
12929             if(tasks.length < 1){
12930                 stopThread();
12931                 return;
12932             }
12933         }
12934         var now = new Date().getTime();
12935         for(var i = 0, len = tasks.length; i < len; ++i){
12936             var t = tasks[i];
12937             var itime = now - t.taskRunTime;
12938             if(t.interval <= itime){
12939                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12940                 t.taskRunTime = now;
12941                 if(rt === false || t.taskRunCount === t.repeat){
12942                     removeTask(t);
12943                     return;
12944                 }
12945             }
12946             if(t.duration && t.duration <= (now - t.taskStartTime)){
12947                 removeTask(t);
12948             }
12949         }
12950     };
12951
12952     /**
12953      * Queues a new task.
12954      * @param {Object} task
12955      */
12956     this.start = function(task){
12957         tasks.push(task);
12958         task.taskStartTime = new Date().getTime();
12959         task.taskRunTime = 0;
12960         task.taskRunCount = 0;
12961         startThread();
12962         return task;
12963     };
12964
12965     this.stop = function(task){
12966         removeTask(task);
12967         return task;
12968     };
12969
12970     this.stopAll = function(){
12971         stopThread();
12972         for(var i = 0, len = tasks.length; i < len; i++){
12973             if(tasks[i].onStop){
12974                 tasks[i].onStop();
12975             }
12976         }
12977         tasks = [];
12978         removeQueue = [];
12979     };
12980 };
12981
12982 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12983  * Based on:
12984  * Ext JS Library 1.1.1
12985  * Copyright(c) 2006-2007, Ext JS, LLC.
12986  *
12987  * Originally Released Under LGPL - original licence link has changed is not relivant.
12988  *
12989  * Fork - LGPL
12990  * <script type="text/javascript">
12991  */
12992
12993  
12994 /**
12995  * @class Roo.util.MixedCollection
12996  * @extends Roo.util.Observable
12997  * A Collection class that maintains both numeric indexes and keys and exposes events.
12998  * @constructor
12999  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13000  * collection (defaults to false)
13001  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13002  * and return the key value for that item.  This is used when available to look up the key on items that
13003  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13004  * equivalent to providing an implementation for the {@link #getKey} method.
13005  */
13006 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13007     this.items = [];
13008     this.map = {};
13009     this.keys = [];
13010     this.length = 0;
13011     this.addEvents({
13012         /**
13013          * @event clear
13014          * Fires when the collection is cleared.
13015          */
13016         "clear" : true,
13017         /**
13018          * @event add
13019          * Fires when an item is added to the collection.
13020          * @param {Number} index The index at which the item was added.
13021          * @param {Object} o The item added.
13022          * @param {String} key The key associated with the added item.
13023          */
13024         "add" : true,
13025         /**
13026          * @event replace
13027          * Fires when an item is replaced in the collection.
13028          * @param {String} key he key associated with the new added.
13029          * @param {Object} old The item being replaced.
13030          * @param {Object} new The new item.
13031          */
13032         "replace" : true,
13033         /**
13034          * @event remove
13035          * Fires when an item is removed from the collection.
13036          * @param {Object} o The item being removed.
13037          * @param {String} key (optional) The key associated with the removed item.
13038          */
13039         "remove" : true,
13040         "sort" : true
13041     });
13042     this.allowFunctions = allowFunctions === true;
13043     if(keyFn){
13044         this.getKey = keyFn;
13045     }
13046     Roo.util.MixedCollection.superclass.constructor.call(this);
13047 };
13048
13049 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13050     allowFunctions : false,
13051     
13052 /**
13053  * Adds an item to the collection.
13054  * @param {String} key The key to associate with the item
13055  * @param {Object} o The item to add.
13056  * @return {Object} The item added.
13057  */
13058     add : function(key, o){
13059         if(arguments.length == 1){
13060             o = arguments[0];
13061             key = this.getKey(o);
13062         }
13063         if(typeof key == "undefined" || key === null){
13064             this.length++;
13065             this.items.push(o);
13066             this.keys.push(null);
13067         }else{
13068             var old = this.map[key];
13069             if(old){
13070                 return this.replace(key, o);
13071             }
13072             this.length++;
13073             this.items.push(o);
13074             this.map[key] = o;
13075             this.keys.push(key);
13076         }
13077         this.fireEvent("add", this.length-1, o, key);
13078         return o;
13079     },
13080        
13081 /**
13082   * MixedCollection has a generic way to fetch keys if you implement getKey.
13083 <pre><code>
13084 // normal way
13085 var mc = new Roo.util.MixedCollection();
13086 mc.add(someEl.dom.id, someEl);
13087 mc.add(otherEl.dom.id, otherEl);
13088 //and so on
13089
13090 // using getKey
13091 var mc = new Roo.util.MixedCollection();
13092 mc.getKey = function(el){
13093    return el.dom.id;
13094 };
13095 mc.add(someEl);
13096 mc.add(otherEl);
13097
13098 // or via the constructor
13099 var mc = new Roo.util.MixedCollection(false, function(el){
13100    return el.dom.id;
13101 });
13102 mc.add(someEl);
13103 mc.add(otherEl);
13104 </code></pre>
13105  * @param o {Object} The item for which to find the key.
13106  * @return {Object} The key for the passed item.
13107  */
13108     getKey : function(o){
13109          return o.id; 
13110     },
13111    
13112 /**
13113  * Replaces an item in the collection.
13114  * @param {String} key The key associated with the item to replace, or the item to replace.
13115  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13116  * @return {Object}  The new item.
13117  */
13118     replace : function(key, o){
13119         if(arguments.length == 1){
13120             o = arguments[0];
13121             key = this.getKey(o);
13122         }
13123         var old = this.item(key);
13124         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13125              return this.add(key, o);
13126         }
13127         var index = this.indexOfKey(key);
13128         this.items[index] = o;
13129         this.map[key] = o;
13130         this.fireEvent("replace", key, old, o);
13131         return o;
13132     },
13133    
13134 /**
13135  * Adds all elements of an Array or an Object to the collection.
13136  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13137  * an Array of values, each of which are added to the collection.
13138  */
13139     addAll : function(objs){
13140         if(arguments.length > 1 || objs instanceof Array){
13141             var args = arguments.length > 1 ? arguments : objs;
13142             for(var i = 0, len = args.length; i < len; i++){
13143                 this.add(args[i]);
13144             }
13145         }else{
13146             for(var key in objs){
13147                 if(this.allowFunctions || typeof objs[key] != "function"){
13148                     this.add(key, objs[key]);
13149                 }
13150             }
13151         }
13152     },
13153    
13154 /**
13155  * Executes the specified function once for every item in the collection, passing each
13156  * item as the first and only parameter. returning false from the function will stop the iteration.
13157  * @param {Function} fn The function to execute for each item.
13158  * @param {Object} scope (optional) The scope in which to execute the function.
13159  */
13160     each : function(fn, scope){
13161         var items = [].concat(this.items); // each safe for removal
13162         for(var i = 0, len = items.length; i < len; i++){
13163             if(fn.call(scope || items[i], items[i], i, len) === false){
13164                 break;
13165             }
13166         }
13167     },
13168    
13169 /**
13170  * Executes the specified function once for every key in the collection, passing each
13171  * key, and its associated item as the first two parameters.
13172  * @param {Function} fn The function to execute for each item.
13173  * @param {Object} scope (optional) The scope in which to execute the function.
13174  */
13175     eachKey : function(fn, scope){
13176         for(var i = 0, len = this.keys.length; i < len; i++){
13177             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13178         }
13179     },
13180    
13181 /**
13182  * Returns the first item in the collection which elicits a true return value from the
13183  * passed selection function.
13184  * @param {Function} fn The selection function to execute for each item.
13185  * @param {Object} scope (optional) The scope in which to execute the function.
13186  * @return {Object} The first item in the collection which returned true from the selection function.
13187  */
13188     find : function(fn, scope){
13189         for(var i = 0, len = this.items.length; i < len; i++){
13190             if(fn.call(scope || window, this.items[i], this.keys[i])){
13191                 return this.items[i];
13192             }
13193         }
13194         return null;
13195     },
13196    
13197 /**
13198  * Inserts an item at the specified index in the collection.
13199  * @param {Number} index The index to insert the item at.
13200  * @param {String} key The key to associate with the new item, or the item itself.
13201  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13202  * @return {Object} The item inserted.
13203  */
13204     insert : function(index, key, o){
13205         if(arguments.length == 2){
13206             o = arguments[1];
13207             key = this.getKey(o);
13208         }
13209         if(index >= this.length){
13210             return this.add(key, o);
13211         }
13212         this.length++;
13213         this.items.splice(index, 0, o);
13214         if(typeof key != "undefined" && key != null){
13215             this.map[key] = o;
13216         }
13217         this.keys.splice(index, 0, key);
13218         this.fireEvent("add", index, o, key);
13219         return o;
13220     },
13221    
13222 /**
13223  * Removed an item from the collection.
13224  * @param {Object} o The item to remove.
13225  * @return {Object} The item removed.
13226  */
13227     remove : function(o){
13228         return this.removeAt(this.indexOf(o));
13229     },
13230    
13231 /**
13232  * Remove an item from a specified index in the collection.
13233  * @param {Number} index The index within the collection of the item to remove.
13234  */
13235     removeAt : function(index){
13236         if(index < this.length && index >= 0){
13237             this.length--;
13238             var o = this.items[index];
13239             this.items.splice(index, 1);
13240             var key = this.keys[index];
13241             if(typeof key != "undefined"){
13242                 delete this.map[key];
13243             }
13244             this.keys.splice(index, 1);
13245             this.fireEvent("remove", o, key);
13246         }
13247     },
13248    
13249 /**
13250  * Removed an item associated with the passed key fom the collection.
13251  * @param {String} key The key of the item to remove.
13252  */
13253     removeKey : function(key){
13254         return this.removeAt(this.indexOfKey(key));
13255     },
13256    
13257 /**
13258  * Returns the number of items in the collection.
13259  * @return {Number} the number of items in the collection.
13260  */
13261     getCount : function(){
13262         return this.length; 
13263     },
13264    
13265 /**
13266  * Returns index within the collection of the passed Object.
13267  * @param {Object} o The item to find the index of.
13268  * @return {Number} index of the item.
13269  */
13270     indexOf : function(o){
13271         if(!this.items.indexOf){
13272             for(var i = 0, len = this.items.length; i < len; i++){
13273                 if(this.items[i] == o) {
13274                     return i;
13275                 }
13276             }
13277             return -1;
13278         }else{
13279             return this.items.indexOf(o);
13280         }
13281     },
13282    
13283 /**
13284  * Returns index within the collection of the passed key.
13285  * @param {String} key The key to find the index of.
13286  * @return {Number} index of the key.
13287  */
13288     indexOfKey : function(key){
13289         if(!this.keys.indexOf){
13290             for(var i = 0, len = this.keys.length; i < len; i++){
13291                 if(this.keys[i] == key) {
13292                     return i;
13293                 }
13294             }
13295             return -1;
13296         }else{
13297             return this.keys.indexOf(key);
13298         }
13299     },
13300    
13301 /**
13302  * Returns the item associated with the passed key OR index. Key has priority over index.
13303  * @param {String/Number} key The key or index of the item.
13304  * @return {Object} The item associated with the passed key.
13305  */
13306     item : function(key){
13307         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13308         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13309     },
13310     
13311 /**
13312  * Returns the item at the specified index.
13313  * @param {Number} index The index of the item.
13314  * @return {Object}
13315  */
13316     itemAt : function(index){
13317         return this.items[index];
13318     },
13319     
13320 /**
13321  * Returns the item associated with the passed key.
13322  * @param {String/Number} key The key of the item.
13323  * @return {Object} The item associated with the passed key.
13324  */
13325     key : function(key){
13326         return this.map[key];
13327     },
13328    
13329 /**
13330  * Returns true if the collection contains the passed Object as an item.
13331  * @param {Object} o  The Object to look for in the collection.
13332  * @return {Boolean} True if the collection contains the Object as an item.
13333  */
13334     contains : function(o){
13335         return this.indexOf(o) != -1;
13336     },
13337    
13338 /**
13339  * Returns true if the collection contains the passed Object as a key.
13340  * @param {String} key The key to look for in the collection.
13341  * @return {Boolean} True if the collection contains the Object as a key.
13342  */
13343     containsKey : function(key){
13344         return typeof this.map[key] != "undefined";
13345     },
13346    
13347 /**
13348  * Removes all items from the collection.
13349  */
13350     clear : function(){
13351         this.length = 0;
13352         this.items = [];
13353         this.keys = [];
13354         this.map = {};
13355         this.fireEvent("clear");
13356     },
13357    
13358 /**
13359  * Returns the first item in the collection.
13360  * @return {Object} the first item in the collection..
13361  */
13362     first : function(){
13363         return this.items[0]; 
13364     },
13365    
13366 /**
13367  * Returns the last item in the collection.
13368  * @return {Object} the last item in the collection..
13369  */
13370     last : function(){
13371         return this.items[this.length-1];   
13372     },
13373     
13374     _sort : function(property, dir, fn){
13375         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13376         fn = fn || function(a, b){
13377             return a-b;
13378         };
13379         var c = [], k = this.keys, items = this.items;
13380         for(var i = 0, len = items.length; i < len; i++){
13381             c[c.length] = {key: k[i], value: items[i], index: i};
13382         }
13383         c.sort(function(a, b){
13384             var v = fn(a[property], b[property]) * dsc;
13385             if(v == 0){
13386                 v = (a.index < b.index ? -1 : 1);
13387             }
13388             return v;
13389         });
13390         for(var i = 0, len = c.length; i < len; i++){
13391             items[i] = c[i].value;
13392             k[i] = c[i].key;
13393         }
13394         this.fireEvent("sort", this);
13395     },
13396     
13397     /**
13398      * Sorts this collection with the passed comparison function
13399      * @param {String} direction (optional) "ASC" or "DESC"
13400      * @param {Function} fn (optional) comparison function
13401      */
13402     sort : function(dir, fn){
13403         this._sort("value", dir, fn);
13404     },
13405     
13406     /**
13407      * Sorts this collection by keys
13408      * @param {String} direction (optional) "ASC" or "DESC"
13409      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13410      */
13411     keySort : function(dir, fn){
13412         this._sort("key", dir, fn || function(a, b){
13413             return String(a).toUpperCase()-String(b).toUpperCase();
13414         });
13415     },
13416     
13417     /**
13418      * Returns a range of items in this collection
13419      * @param {Number} startIndex (optional) defaults to 0
13420      * @param {Number} endIndex (optional) default to the last item
13421      * @return {Array} An array of items
13422      */
13423     getRange : function(start, end){
13424         var items = this.items;
13425         if(items.length < 1){
13426             return [];
13427         }
13428         start = start || 0;
13429         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13430         var r = [];
13431         if(start <= end){
13432             for(var i = start; i <= end; i++) {
13433                     r[r.length] = items[i];
13434             }
13435         }else{
13436             for(var i = start; i >= end; i--) {
13437                     r[r.length] = items[i];
13438             }
13439         }
13440         return r;
13441     },
13442         
13443     /**
13444      * Filter the <i>objects</i> in this collection by a specific property. 
13445      * Returns a new collection that has been filtered.
13446      * @param {String} property A property on your objects
13447      * @param {String/RegExp} value Either string that the property values 
13448      * should start with or a RegExp to test against the property
13449      * @return {MixedCollection} The new filtered collection
13450      */
13451     filter : function(property, value){
13452         if(!value.exec){ // not a regex
13453             value = String(value);
13454             if(value.length == 0){
13455                 return this.clone();
13456             }
13457             value = new RegExp("^" + Roo.escapeRe(value), "i");
13458         }
13459         return this.filterBy(function(o){
13460             return o && value.test(o[property]);
13461         });
13462         },
13463     
13464     /**
13465      * Filter by a function. * Returns a new collection that has been filtered.
13466      * The passed function will be called with each 
13467      * object in the collection. If the function returns true, the value is included 
13468      * otherwise it is filtered.
13469      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13470      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13471      * @return {MixedCollection} The new filtered collection
13472      */
13473     filterBy : function(fn, scope){
13474         var r = new Roo.util.MixedCollection();
13475         r.getKey = this.getKey;
13476         var k = this.keys, it = this.items;
13477         for(var i = 0, len = it.length; i < len; i++){
13478             if(fn.call(scope||this, it[i], k[i])){
13479                                 r.add(k[i], it[i]);
13480                         }
13481         }
13482         return r;
13483     },
13484     
13485     /**
13486      * Creates a duplicate of this collection
13487      * @return {MixedCollection}
13488      */
13489     clone : function(){
13490         var r = new Roo.util.MixedCollection();
13491         var k = this.keys, it = this.items;
13492         for(var i = 0, len = it.length; i < len; i++){
13493             r.add(k[i], it[i]);
13494         }
13495         r.getKey = this.getKey;
13496         return r;
13497     }
13498 });
13499 /**
13500  * Returns the item associated with the passed key or index.
13501  * @method
13502  * @param {String/Number} key The key or index of the item.
13503  * @return {Object} The item associated with the passed key.
13504  */
13505 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13506  * Based on:
13507  * Ext JS Library 1.1.1
13508  * Copyright(c) 2006-2007, Ext JS, LLC.
13509  *
13510  * Originally Released Under LGPL - original licence link has changed is not relivant.
13511  *
13512  * Fork - LGPL
13513  * <script type="text/javascript">
13514  */
13515 /**
13516  * @class Roo.util.JSON
13517  * Modified version of Douglas Crockford"s json.js that doesn"t
13518  * mess with the Object prototype 
13519  * http://www.json.org/js.html
13520  * @singleton
13521  */
13522 Roo.util.JSON = new (function(){
13523     var useHasOwn = {}.hasOwnProperty ? true : false;
13524     
13525     // crashes Safari in some instances
13526     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13527     
13528     var pad = function(n) {
13529         return n < 10 ? "0" + n : n;
13530     };
13531     
13532     var m = {
13533         "\b": '\\b',
13534         "\t": '\\t',
13535         "\n": '\\n',
13536         "\f": '\\f',
13537         "\r": '\\r',
13538         '"' : '\\"',
13539         "\\": '\\\\'
13540     };
13541
13542     var encodeString = function(s){
13543         if (/["\\\x00-\x1f]/.test(s)) {
13544             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13545                 var c = m[b];
13546                 if(c){
13547                     return c;
13548                 }
13549                 c = b.charCodeAt();
13550                 return "\\u00" +
13551                     Math.floor(c / 16).toString(16) +
13552                     (c % 16).toString(16);
13553             }) + '"';
13554         }
13555         return '"' + s + '"';
13556     };
13557     
13558     var encodeArray = function(o){
13559         var a = ["["], b, i, l = o.length, v;
13560             for (i = 0; i < l; i += 1) {
13561                 v = o[i];
13562                 switch (typeof v) {
13563                     case "undefined":
13564                     case "function":
13565                     case "unknown":
13566                         break;
13567                     default:
13568                         if (b) {
13569                             a.push(',');
13570                         }
13571                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13572                         b = true;
13573                 }
13574             }
13575             a.push("]");
13576             return a.join("");
13577     };
13578     
13579     var encodeDate = function(o){
13580         return '"' + o.getFullYear() + "-" +
13581                 pad(o.getMonth() + 1) + "-" +
13582                 pad(o.getDate()) + "T" +
13583                 pad(o.getHours()) + ":" +
13584                 pad(o.getMinutes()) + ":" +
13585                 pad(o.getSeconds()) + '"';
13586     };
13587     
13588     /**
13589      * Encodes an Object, Array or other value
13590      * @param {Mixed} o The variable to encode
13591      * @return {String} The JSON string
13592      */
13593     this.encode = function(o)
13594     {
13595         // should this be extended to fully wrap stringify..
13596         
13597         if(typeof o == "undefined" || o === null){
13598             return "null";
13599         }else if(o instanceof Array){
13600             return encodeArray(o);
13601         }else if(o instanceof Date){
13602             return encodeDate(o);
13603         }else if(typeof o == "string"){
13604             return encodeString(o);
13605         }else if(typeof o == "number"){
13606             return isFinite(o) ? String(o) : "null";
13607         }else if(typeof o == "boolean"){
13608             return String(o);
13609         }else {
13610             var a = ["{"], b, i, v;
13611             for (i in o) {
13612                 if(!useHasOwn || o.hasOwnProperty(i)) {
13613                     v = o[i];
13614                     switch (typeof v) {
13615                     case "undefined":
13616                     case "function":
13617                     case "unknown":
13618                         break;
13619                     default:
13620                         if(b){
13621                             a.push(',');
13622                         }
13623                         a.push(this.encode(i), ":",
13624                                 v === null ? "null" : this.encode(v));
13625                         b = true;
13626                     }
13627                 }
13628             }
13629             a.push("}");
13630             return a.join("");
13631         }
13632     };
13633     
13634     /**
13635      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13636      * @param {String} json The JSON string
13637      * @return {Object} The resulting object
13638      */
13639     this.decode = function(json){
13640         
13641         return  /** eval:var:json */ eval("(" + json + ')');
13642     };
13643 })();
13644 /** 
13645  * Shorthand for {@link Roo.util.JSON#encode}
13646  * @member Roo encode 
13647  * @method */
13648 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13649 /** 
13650  * Shorthand for {@link Roo.util.JSON#decode}
13651  * @member Roo decode 
13652  * @method */
13653 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13654 /*
13655  * Based on:
13656  * Ext JS Library 1.1.1
13657  * Copyright(c) 2006-2007, Ext JS, LLC.
13658  *
13659  * Originally Released Under LGPL - original licence link has changed is not relivant.
13660  *
13661  * Fork - LGPL
13662  * <script type="text/javascript">
13663  */
13664  
13665 /**
13666  * @class Roo.util.Format
13667  * Reusable data formatting functions
13668  * @singleton
13669  */
13670 Roo.util.Format = function(){
13671     var trimRe = /^\s+|\s+$/g;
13672     return {
13673         /**
13674          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13675          * @param {String} value The string to truncate
13676          * @param {Number} length The maximum length to allow before truncating
13677          * @return {String} The converted text
13678          */
13679         ellipsis : function(value, len){
13680             if(value && value.length > len){
13681                 return value.substr(0, len-3)+"...";
13682             }
13683             return value;
13684         },
13685
13686         /**
13687          * Checks a reference and converts it to empty string if it is undefined
13688          * @param {Mixed} value Reference to check
13689          * @return {Mixed} Empty string if converted, otherwise the original value
13690          */
13691         undef : function(value){
13692             return typeof value != "undefined" ? value : "";
13693         },
13694
13695         /**
13696          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13697          * @param {String} value The string to encode
13698          * @return {String} The encoded text
13699          */
13700         htmlEncode : function(value){
13701             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13702         },
13703
13704         /**
13705          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13706          * @param {String} value The string to decode
13707          * @return {String} The decoded text
13708          */
13709         htmlDecode : function(value){
13710             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13711         },
13712
13713         /**
13714          * Trims any whitespace from either side of a string
13715          * @param {String} value The text to trim
13716          * @return {String} The trimmed text
13717          */
13718         trim : function(value){
13719             return String(value).replace(trimRe, "");
13720         },
13721
13722         /**
13723          * Returns a substring from within an original string
13724          * @param {String} value The original text
13725          * @param {Number} start The start index of the substring
13726          * @param {Number} length The length of the substring
13727          * @return {String} The substring
13728          */
13729         substr : function(value, start, length){
13730             return String(value).substr(start, length);
13731         },
13732
13733         /**
13734          * Converts a string to all lower case letters
13735          * @param {String} value The text to convert
13736          * @return {String} The converted text
13737          */
13738         lowercase : function(value){
13739             return String(value).toLowerCase();
13740         },
13741
13742         /**
13743          * Converts a string to all upper case letters
13744          * @param {String} value The text to convert
13745          * @return {String} The converted text
13746          */
13747         uppercase : function(value){
13748             return String(value).toUpperCase();
13749         },
13750
13751         /**
13752          * Converts the first character only of a string to upper case
13753          * @param {String} value The text to convert
13754          * @return {String} The converted text
13755          */
13756         capitalize : function(value){
13757             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13758         },
13759
13760         // private
13761         call : function(value, fn){
13762             if(arguments.length > 2){
13763                 var args = Array.prototype.slice.call(arguments, 2);
13764                 args.unshift(value);
13765                  
13766                 return /** eval:var:value */  eval(fn).apply(window, args);
13767             }else{
13768                 /** eval:var:value */
13769                 return /** eval:var:value */ eval(fn).call(window, value);
13770             }
13771         },
13772
13773        
13774         /**
13775          * safer version of Math.toFixed..??/
13776          * @param {Number/String} value The numeric value to format
13777          * @param {Number/String} value Decimal places 
13778          * @return {String} The formatted currency string
13779          */
13780         toFixed : function(v, n)
13781         {
13782             // why not use to fixed - precision is buggered???
13783             if (!n) {
13784                 return Math.round(v-0);
13785             }
13786             var fact = Math.pow(10,n+1);
13787             v = (Math.round((v-0)*fact))/fact;
13788             var z = (''+fact).substring(2);
13789             if (v == Math.floor(v)) {
13790                 return Math.floor(v) + '.' + z;
13791             }
13792             
13793             // now just padd decimals..
13794             var ps = String(v).split('.');
13795             var fd = (ps[1] + z);
13796             var r = fd.substring(0,n); 
13797             var rm = fd.substring(n); 
13798             if (rm < 5) {
13799                 return ps[0] + '.' + r;
13800             }
13801             r*=1; // turn it into a number;
13802             r++;
13803             if (String(r).length != n) {
13804                 ps[0]*=1;
13805                 ps[0]++;
13806                 r = String(r).substring(1); // chop the end off.
13807             }
13808             
13809             return ps[0] + '.' + r;
13810              
13811         },
13812         
13813         /**
13814          * Format a number as US currency
13815          * @param {Number/String} value The numeric value to format
13816          * @return {String} The formatted currency string
13817          */
13818         usMoney : function(v){
13819             return '$' + Roo.util.Format.number(v);
13820         },
13821         
13822         /**
13823          * Format a number
13824          * eventually this should probably emulate php's number_format
13825          * @param {Number/String} value The numeric value to format
13826          * @param {Number} decimals number of decimal places
13827          * @param {String} delimiter for thousands (default comma)
13828          * @return {String} The formatted currency string
13829          */
13830         number : function(v, decimals, thousandsDelimiter)
13831         {
13832             // multiply and round.
13833             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13834             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13835             
13836             var mul = Math.pow(10, decimals);
13837             var zero = String(mul).substring(1);
13838             v = (Math.round((v-0)*mul))/mul;
13839             
13840             // if it's '0' number.. then
13841             
13842             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13843             v = String(v);
13844             var ps = v.split('.');
13845             var whole = ps[0];
13846             
13847             var r = /(\d+)(\d{3})/;
13848             // add comma's
13849             
13850             if(thousandsDelimiter.length != 0) {
13851                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13852             } 
13853             
13854             var sub = ps[1] ?
13855                     // has decimals..
13856                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13857                     // does not have decimals
13858                     (decimals ? ('.' + zero) : '');
13859             
13860             
13861             return whole + sub ;
13862         },
13863         
13864         /**
13865          * Parse a value into a formatted date using the specified format pattern.
13866          * @param {Mixed} value The value to format
13867          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13868          * @return {String} The formatted date string
13869          */
13870         date : function(v, format){
13871             if(!v){
13872                 return "";
13873             }
13874             if(!(v instanceof Date)){
13875                 v = new Date(Date.parse(v));
13876             }
13877             return v.dateFormat(format || Roo.util.Format.defaults.date);
13878         },
13879
13880         /**
13881          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13882          * @param {String} format Any valid date format string
13883          * @return {Function} The date formatting function
13884          */
13885         dateRenderer : function(format){
13886             return function(v){
13887                 return Roo.util.Format.date(v, format);  
13888             };
13889         },
13890
13891         // private
13892         stripTagsRE : /<\/?[^>]+>/gi,
13893         
13894         /**
13895          * Strips all HTML tags
13896          * @param {Mixed} value The text from which to strip tags
13897          * @return {String} The stripped text
13898          */
13899         stripTags : function(v){
13900             return !v ? v : String(v).replace(this.stripTagsRE, "");
13901         }
13902     };
13903 }();
13904 Roo.util.Format.defaults = {
13905     date : 'd/M/Y'
13906 };/*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916
13917
13918  
13919
13920 /**
13921  * @class Roo.MasterTemplate
13922  * @extends Roo.Template
13923  * Provides a template that can have child templates. The syntax is:
13924 <pre><code>
13925 var t = new Roo.MasterTemplate(
13926         '&lt;select name="{name}"&gt;',
13927                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13928         '&lt;/select&gt;'
13929 );
13930 t.add('options', {value: 'foo', text: 'bar'});
13931 // or you can add multiple child elements in one shot
13932 t.addAll('options', [
13933     {value: 'foo', text: 'bar'},
13934     {value: 'foo2', text: 'bar2'},
13935     {value: 'foo3', text: 'bar3'}
13936 ]);
13937 // then append, applying the master template values
13938 t.append('my-form', {name: 'my-select'});
13939 </code></pre>
13940 * A name attribute for the child template is not required if you have only one child
13941 * template or you want to refer to them by index.
13942  */
13943 Roo.MasterTemplate = function(){
13944     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13945     this.originalHtml = this.html;
13946     var st = {};
13947     var m, re = this.subTemplateRe;
13948     re.lastIndex = 0;
13949     var subIndex = 0;
13950     while(m = re.exec(this.html)){
13951         var name = m[1], content = m[2];
13952         st[subIndex] = {
13953             name: name,
13954             index: subIndex,
13955             buffer: [],
13956             tpl : new Roo.Template(content)
13957         };
13958         if(name){
13959             st[name] = st[subIndex];
13960         }
13961         st[subIndex].tpl.compile();
13962         st[subIndex].tpl.call = this.call.createDelegate(this);
13963         subIndex++;
13964     }
13965     this.subCount = subIndex;
13966     this.subs = st;
13967 };
13968 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13969     /**
13970     * The regular expression used to match sub templates
13971     * @type RegExp
13972     * @property
13973     */
13974     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13975
13976     /**
13977      * Applies the passed values to a child template.
13978      * @param {String/Number} name (optional) The name or index of the child template
13979      * @param {Array/Object} values The values to be applied to the template
13980      * @return {MasterTemplate} this
13981      */
13982      add : function(name, values){
13983         if(arguments.length == 1){
13984             values = arguments[0];
13985             name = 0;
13986         }
13987         var s = this.subs[name];
13988         s.buffer[s.buffer.length] = s.tpl.apply(values);
13989         return this;
13990     },
13991
13992     /**
13993      * Applies all the passed values to a child template.
13994      * @param {String/Number} name (optional) The name or index of the child template
13995      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13996      * @param {Boolean} reset (optional) True to reset the template first
13997      * @return {MasterTemplate} this
13998      */
13999     fill : function(name, values, reset){
14000         var a = arguments;
14001         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14002             values = a[0];
14003             name = 0;
14004             reset = a[1];
14005         }
14006         if(reset){
14007             this.reset();
14008         }
14009         for(var i = 0, len = values.length; i < len; i++){
14010             this.add(name, values[i]);
14011         }
14012         return this;
14013     },
14014
14015     /**
14016      * Resets the template for reuse
14017      * @return {MasterTemplate} this
14018      */
14019      reset : function(){
14020         var s = this.subs;
14021         for(var i = 0; i < this.subCount; i++){
14022             s[i].buffer = [];
14023         }
14024         return this;
14025     },
14026
14027     applyTemplate : function(values){
14028         var s = this.subs;
14029         var replaceIndex = -1;
14030         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14031             return s[++replaceIndex].buffer.join("");
14032         });
14033         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14034     },
14035
14036     apply : function(){
14037         return this.applyTemplate.apply(this, arguments);
14038     },
14039
14040     compile : function(){return this;}
14041 });
14042
14043 /**
14044  * Alias for fill().
14045  * @method
14046  */
14047 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14048  /**
14049  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14050  * var tpl = Roo.MasterTemplate.from('element-id');
14051  * @param {String/HTMLElement} el
14052  * @param {Object} config
14053  * @static
14054  */
14055 Roo.MasterTemplate.from = function(el, config){
14056     el = Roo.getDom(el);
14057     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14058 };/*
14059  * Based on:
14060  * Ext JS Library 1.1.1
14061  * Copyright(c) 2006-2007, Ext JS, LLC.
14062  *
14063  * Originally Released Under LGPL - original licence link has changed is not relivant.
14064  *
14065  * Fork - LGPL
14066  * <script type="text/javascript">
14067  */
14068
14069  
14070 /**
14071  * @class Roo.util.CSS
14072  * Utility class for manipulating CSS rules
14073  * @singleton
14074  */
14075 Roo.util.CSS = function(){
14076         var rules = null;
14077         var doc = document;
14078
14079     var camelRe = /(-[a-z])/gi;
14080     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14081
14082    return {
14083    /**
14084     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14085     * tag and appended to the HEAD of the document.
14086     * @param {String|Object} cssText The text containing the css rules
14087     * @param {String} id An id to add to the stylesheet for later removal
14088     * @return {StyleSheet}
14089     */
14090     createStyleSheet : function(cssText, id){
14091         var ss;
14092         var head = doc.getElementsByTagName("head")[0];
14093         var nrules = doc.createElement("style");
14094         nrules.setAttribute("type", "text/css");
14095         if(id){
14096             nrules.setAttribute("id", id);
14097         }
14098         if (typeof(cssText) != 'string') {
14099             // support object maps..
14100             // not sure if this a good idea.. 
14101             // perhaps it should be merged with the general css handling
14102             // and handle js style props.
14103             var cssTextNew = [];
14104             for(var n in cssText) {
14105                 var citems = [];
14106                 for(var k in cssText[n]) {
14107                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14108                 }
14109                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14110                 
14111             }
14112             cssText = cssTextNew.join("\n");
14113             
14114         }
14115        
14116        
14117        if(Roo.isIE){
14118            head.appendChild(nrules);
14119            ss = nrules.styleSheet;
14120            ss.cssText = cssText;
14121        }else{
14122            try{
14123                 nrules.appendChild(doc.createTextNode(cssText));
14124            }catch(e){
14125                nrules.cssText = cssText; 
14126            }
14127            head.appendChild(nrules);
14128            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14129        }
14130        this.cacheStyleSheet(ss);
14131        return ss;
14132    },
14133
14134    /**
14135     * Removes a style or link tag by id
14136     * @param {String} id The id of the tag
14137     */
14138    removeStyleSheet : function(id){
14139        var existing = doc.getElementById(id);
14140        if(existing){
14141            existing.parentNode.removeChild(existing);
14142        }
14143    },
14144
14145    /**
14146     * Dynamically swaps an existing stylesheet reference for a new one
14147     * @param {String} id The id of an existing link tag to remove
14148     * @param {String} url The href of the new stylesheet to include
14149     */
14150    swapStyleSheet : function(id, url){
14151        this.removeStyleSheet(id);
14152        var ss = doc.createElement("link");
14153        ss.setAttribute("rel", "stylesheet");
14154        ss.setAttribute("type", "text/css");
14155        ss.setAttribute("id", id);
14156        ss.setAttribute("href", url);
14157        doc.getElementsByTagName("head")[0].appendChild(ss);
14158    },
14159    
14160    /**
14161     * Refresh the rule cache if you have dynamically added stylesheets
14162     * @return {Object} An object (hash) of rules indexed by selector
14163     */
14164    refreshCache : function(){
14165        return this.getRules(true);
14166    },
14167
14168    // private
14169    cacheStyleSheet : function(stylesheet){
14170        if(!rules){
14171            rules = {};
14172        }
14173        try{// try catch for cross domain access issue
14174            var ssRules = stylesheet.cssRules || stylesheet.rules;
14175            for(var j = ssRules.length-1; j >= 0; --j){
14176                rules[ssRules[j].selectorText] = ssRules[j];
14177            }
14178        }catch(e){}
14179    },
14180    
14181    /**
14182     * Gets all css rules for the document
14183     * @param {Boolean} refreshCache true to refresh the internal cache
14184     * @return {Object} An object (hash) of rules indexed by selector
14185     */
14186    getRules : function(refreshCache){
14187                 if(rules == null || refreshCache){
14188                         rules = {};
14189                         var ds = doc.styleSheets;
14190                         for(var i =0, len = ds.length; i < len; i++){
14191                             try{
14192                         this.cacheStyleSheet(ds[i]);
14193                     }catch(e){} 
14194                 }
14195                 }
14196                 return rules;
14197         },
14198         
14199         /**
14200     * Gets an an individual CSS rule by selector(s)
14201     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14202     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14203     * @return {CSSRule} The CSS rule or null if one is not found
14204     */
14205    getRule : function(selector, refreshCache){
14206                 var rs = this.getRules(refreshCache);
14207                 if(!(selector instanceof Array)){
14208                     return rs[selector];
14209                 }
14210                 for(var i = 0; i < selector.length; i++){
14211                         if(rs[selector[i]]){
14212                                 return rs[selector[i]];
14213                         }
14214                 }
14215                 return null;
14216         },
14217         
14218         
14219         /**
14220     * Updates a rule property
14221     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14222     * @param {String} property The css property
14223     * @param {String} value The new value for the property
14224     * @return {Boolean} true If a rule was found and updated
14225     */
14226    updateRule : function(selector, property, value){
14227                 if(!(selector instanceof Array)){
14228                         var rule = this.getRule(selector);
14229                         if(rule){
14230                                 rule.style[property.replace(camelRe, camelFn)] = value;
14231                                 return true;
14232                         }
14233                 }else{
14234                         for(var i = 0; i < selector.length; i++){
14235                                 if(this.updateRule(selector[i], property, value)){
14236                                         return true;
14237                                 }
14238                         }
14239                 }
14240                 return false;
14241         }
14242    };   
14243 }();/*
14244  * Based on:
14245  * Ext JS Library 1.1.1
14246  * Copyright(c) 2006-2007, Ext JS, LLC.
14247  *
14248  * Originally Released Under LGPL - original licence link has changed is not relivant.
14249  *
14250  * Fork - LGPL
14251  * <script type="text/javascript">
14252  */
14253
14254  
14255
14256 /**
14257  * @class Roo.util.ClickRepeater
14258  * @extends Roo.util.Observable
14259  * 
14260  * A wrapper class which can be applied to any element. Fires a "click" event while the
14261  * mouse is pressed. The interval between firings may be specified in the config but
14262  * defaults to 10 milliseconds.
14263  * 
14264  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14265  * 
14266  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14267  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14268  * Similar to an autorepeat key delay.
14269  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14270  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14271  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14272  *           "interval" and "delay" are ignored. "immediate" is honored.
14273  * @cfg {Boolean} preventDefault True to prevent the default click event
14274  * @cfg {Boolean} stopDefault True to stop the default click event
14275  * 
14276  * @history
14277  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14278  *     2007-02-02 jvs Renamed to ClickRepeater
14279  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14280  *
14281  *  @constructor
14282  * @param {String/HTMLElement/Element} el The element to listen on
14283  * @param {Object} config
14284  **/
14285 Roo.util.ClickRepeater = function(el, config)
14286 {
14287     this.el = Roo.get(el);
14288     this.el.unselectable();
14289
14290     Roo.apply(this, config);
14291
14292     this.addEvents({
14293     /**
14294      * @event mousedown
14295      * Fires when the mouse button is depressed.
14296      * @param {Roo.util.ClickRepeater} this
14297      */
14298         "mousedown" : true,
14299     /**
14300      * @event click
14301      * Fires on a specified interval during the time the element is pressed.
14302      * @param {Roo.util.ClickRepeater} this
14303      */
14304         "click" : true,
14305     /**
14306      * @event mouseup
14307      * Fires when the mouse key is released.
14308      * @param {Roo.util.ClickRepeater} this
14309      */
14310         "mouseup" : true
14311     });
14312
14313     this.el.on("mousedown", this.handleMouseDown, this);
14314     if(this.preventDefault || this.stopDefault){
14315         this.el.on("click", function(e){
14316             if(this.preventDefault){
14317                 e.preventDefault();
14318             }
14319             if(this.stopDefault){
14320                 e.stopEvent();
14321             }
14322         }, this);
14323     }
14324
14325     // allow inline handler
14326     if(this.handler){
14327         this.on("click", this.handler,  this.scope || this);
14328     }
14329
14330     Roo.util.ClickRepeater.superclass.constructor.call(this);
14331 };
14332
14333 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14334     interval : 20,
14335     delay: 250,
14336     preventDefault : true,
14337     stopDefault : false,
14338     timer : 0,
14339
14340     // private
14341     handleMouseDown : function(){
14342         clearTimeout(this.timer);
14343         this.el.blur();
14344         if(this.pressClass){
14345             this.el.addClass(this.pressClass);
14346         }
14347         this.mousedownTime = new Date();
14348
14349         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14350         this.el.on("mouseout", this.handleMouseOut, this);
14351
14352         this.fireEvent("mousedown", this);
14353         this.fireEvent("click", this);
14354         
14355         this.timer = this.click.defer(this.delay || this.interval, this);
14356     },
14357
14358     // private
14359     click : function(){
14360         this.fireEvent("click", this);
14361         this.timer = this.click.defer(this.getInterval(), this);
14362     },
14363
14364     // private
14365     getInterval: function(){
14366         if(!this.accelerate){
14367             return this.interval;
14368         }
14369         var pressTime = this.mousedownTime.getElapsed();
14370         if(pressTime < 500){
14371             return 400;
14372         }else if(pressTime < 1700){
14373             return 320;
14374         }else if(pressTime < 2600){
14375             return 250;
14376         }else if(pressTime < 3500){
14377             return 180;
14378         }else if(pressTime < 4400){
14379             return 140;
14380         }else if(pressTime < 5300){
14381             return 80;
14382         }else if(pressTime < 6200){
14383             return 50;
14384         }else{
14385             return 10;
14386         }
14387     },
14388
14389     // private
14390     handleMouseOut : function(){
14391         clearTimeout(this.timer);
14392         if(this.pressClass){
14393             this.el.removeClass(this.pressClass);
14394         }
14395         this.el.on("mouseover", this.handleMouseReturn, this);
14396     },
14397
14398     // private
14399     handleMouseReturn : function(){
14400         this.el.un("mouseover", this.handleMouseReturn);
14401         if(this.pressClass){
14402             this.el.addClass(this.pressClass);
14403         }
14404         this.click();
14405     },
14406
14407     // private
14408     handleMouseUp : function(){
14409         clearTimeout(this.timer);
14410         this.el.un("mouseover", this.handleMouseReturn);
14411         this.el.un("mouseout", this.handleMouseOut);
14412         Roo.get(document).un("mouseup", this.handleMouseUp);
14413         this.el.removeClass(this.pressClass);
14414         this.fireEvent("mouseup", this);
14415     }
14416 });/*
14417  * Based on:
14418  * Ext JS Library 1.1.1
14419  * Copyright(c) 2006-2007, Ext JS, LLC.
14420  *
14421  * Originally Released Under LGPL - original licence link has changed is not relivant.
14422  *
14423  * Fork - LGPL
14424  * <script type="text/javascript">
14425  */
14426
14427  
14428 /**
14429  * @class Roo.KeyNav
14430  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14431  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14432  * way to implement custom navigation schemes for any UI component.</p>
14433  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14434  * pageUp, pageDown, del, home, end.  Usage:</p>
14435  <pre><code>
14436 var nav = new Roo.KeyNav("my-element", {
14437     "left" : function(e){
14438         this.moveLeft(e.ctrlKey);
14439     },
14440     "right" : function(e){
14441         this.moveRight(e.ctrlKey);
14442     },
14443     "enter" : function(e){
14444         this.save();
14445     },
14446     scope : this
14447 });
14448 </code></pre>
14449  * @constructor
14450  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14451  * @param {Object} config The config
14452  */
14453 Roo.KeyNav = function(el, config){
14454     this.el = Roo.get(el);
14455     Roo.apply(this, config);
14456     if(!this.disabled){
14457         this.disabled = true;
14458         this.enable();
14459     }
14460 };
14461
14462 Roo.KeyNav.prototype = {
14463     /**
14464      * @cfg {Boolean} disabled
14465      * True to disable this KeyNav instance (defaults to false)
14466      */
14467     disabled : false,
14468     /**
14469      * @cfg {String} defaultEventAction
14470      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14471      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14472      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14473      */
14474     defaultEventAction: "stopEvent",
14475     /**
14476      * @cfg {Boolean} forceKeyDown
14477      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14478      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14479      * handle keydown instead of keypress.
14480      */
14481     forceKeyDown : false,
14482
14483     // private
14484     prepareEvent : function(e){
14485         var k = e.getKey();
14486         var h = this.keyToHandler[k];
14487         //if(h && this[h]){
14488         //    e.stopPropagation();
14489         //}
14490         if(Roo.isSafari && h && k >= 37 && k <= 40){
14491             e.stopEvent();
14492         }
14493     },
14494
14495     // private
14496     relay : function(e){
14497         var k = e.getKey();
14498         var h = this.keyToHandler[k];
14499         if(h && this[h]){
14500             if(this.doRelay(e, this[h], h) !== true){
14501                 e[this.defaultEventAction]();
14502             }
14503         }
14504     },
14505
14506     // private
14507     doRelay : function(e, h, hname){
14508         return h.call(this.scope || this, e);
14509     },
14510
14511     // possible handlers
14512     enter : false,
14513     left : false,
14514     right : false,
14515     up : false,
14516     down : false,
14517     tab : false,
14518     esc : false,
14519     pageUp : false,
14520     pageDown : false,
14521     del : false,
14522     home : false,
14523     end : false,
14524
14525     // quick lookup hash
14526     keyToHandler : {
14527         37 : "left",
14528         39 : "right",
14529         38 : "up",
14530         40 : "down",
14531         33 : "pageUp",
14532         34 : "pageDown",
14533         46 : "del",
14534         36 : "home",
14535         35 : "end",
14536         13 : "enter",
14537         27 : "esc",
14538         9  : "tab"
14539     },
14540
14541         /**
14542          * Enable this KeyNav
14543          */
14544         enable: function(){
14545                 if(this.disabled){
14546             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14547             // the EventObject will normalize Safari automatically
14548             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14549                 this.el.on("keydown", this.relay,  this);
14550             }else{
14551                 this.el.on("keydown", this.prepareEvent,  this);
14552                 this.el.on("keypress", this.relay,  this);
14553             }
14554                     this.disabled = false;
14555                 }
14556         },
14557
14558         /**
14559          * Disable this KeyNav
14560          */
14561         disable: function(){
14562                 if(!this.disabled){
14563                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14564                 this.el.un("keydown", this.relay);
14565             }else{
14566                 this.el.un("keydown", this.prepareEvent);
14567                 this.el.un("keypress", this.relay);
14568             }
14569                     this.disabled = true;
14570                 }
14571         }
14572 };/*
14573  * Based on:
14574  * Ext JS Library 1.1.1
14575  * Copyright(c) 2006-2007, Ext JS, LLC.
14576  *
14577  * Originally Released Under LGPL - original licence link has changed is not relivant.
14578  *
14579  * Fork - LGPL
14580  * <script type="text/javascript">
14581  */
14582
14583  
14584 /**
14585  * @class Roo.KeyMap
14586  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14587  * The constructor accepts the same config object as defined by {@link #addBinding}.
14588  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14589  * combination it will call the function with this signature (if the match is a multi-key
14590  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14591  * A KeyMap can also handle a string representation of keys.<br />
14592  * Usage:
14593  <pre><code>
14594 // map one key by key code
14595 var map = new Roo.KeyMap("my-element", {
14596     key: 13, // or Roo.EventObject.ENTER
14597     fn: myHandler,
14598     scope: myObject
14599 });
14600
14601 // map multiple keys to one action by string
14602 var map = new Roo.KeyMap("my-element", {
14603     key: "a\r\n\t",
14604     fn: myHandler,
14605     scope: myObject
14606 });
14607
14608 // map multiple keys to multiple actions by strings and array of codes
14609 var map = new Roo.KeyMap("my-element", [
14610     {
14611         key: [10,13],
14612         fn: function(){ alert("Return was pressed"); }
14613     }, {
14614         key: "abc",
14615         fn: function(){ alert('a, b or c was pressed'); }
14616     }, {
14617         key: "\t",
14618         ctrl:true,
14619         shift:true,
14620         fn: function(){ alert('Control + shift + tab was pressed.'); }
14621     }
14622 ]);
14623 </code></pre>
14624  * <b>Note: A KeyMap starts enabled</b>
14625  * @constructor
14626  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14627  * @param {Object} config The config (see {@link #addBinding})
14628  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14629  */
14630 Roo.KeyMap = function(el, config, eventName){
14631     this.el  = Roo.get(el);
14632     this.eventName = eventName || "keydown";
14633     this.bindings = [];
14634     if(config){
14635         this.addBinding(config);
14636     }
14637     this.enable();
14638 };
14639
14640 Roo.KeyMap.prototype = {
14641     /**
14642      * True to stop the event from bubbling and prevent the default browser action if the
14643      * key was handled by the KeyMap (defaults to false)
14644      * @type Boolean
14645      */
14646     stopEvent : false,
14647
14648     /**
14649      * Add a new binding to this KeyMap. The following config object properties are supported:
14650      * <pre>
14651 Property    Type             Description
14652 ----------  ---------------  ----------------------------------------------------------------------
14653 key         String/Array     A single keycode or an array of keycodes to handle
14654 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14655 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14656 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14657 fn          Function         The function to call when KeyMap finds the expected key combination
14658 scope       Object           The scope of the callback function
14659 </pre>
14660      *
14661      * Usage:
14662      * <pre><code>
14663 // Create a KeyMap
14664 var map = new Roo.KeyMap(document, {
14665     key: Roo.EventObject.ENTER,
14666     fn: handleKey,
14667     scope: this
14668 });
14669
14670 //Add a new binding to the existing KeyMap later
14671 map.addBinding({
14672     key: 'abc',
14673     shift: true,
14674     fn: handleKey,
14675     scope: this
14676 });
14677 </code></pre>
14678      * @param {Object/Array} config A single KeyMap config or an array of configs
14679      */
14680         addBinding : function(config){
14681         if(config instanceof Array){
14682             for(var i = 0, len = config.length; i < len; i++){
14683                 this.addBinding(config[i]);
14684             }
14685             return;
14686         }
14687         var keyCode = config.key,
14688             shift = config.shift, 
14689             ctrl = config.ctrl, 
14690             alt = config.alt,
14691             fn = config.fn,
14692             scope = config.scope;
14693         if(typeof keyCode == "string"){
14694             var ks = [];
14695             var keyString = keyCode.toUpperCase();
14696             for(var j = 0, len = keyString.length; j < len; j++){
14697                 ks.push(keyString.charCodeAt(j));
14698             }
14699             keyCode = ks;
14700         }
14701         var keyArray = keyCode instanceof Array;
14702         var handler = function(e){
14703             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14704                 var k = e.getKey();
14705                 if(keyArray){
14706                     for(var i = 0, len = keyCode.length; i < len; i++){
14707                         if(keyCode[i] == k){
14708                           if(this.stopEvent){
14709                               e.stopEvent();
14710                           }
14711                           fn.call(scope || window, k, e);
14712                           return;
14713                         }
14714                     }
14715                 }else{
14716                     if(k == keyCode){
14717                         if(this.stopEvent){
14718                            e.stopEvent();
14719                         }
14720                         fn.call(scope || window, k, e);
14721                     }
14722                 }
14723             }
14724         };
14725         this.bindings.push(handler);  
14726         },
14727
14728     /**
14729      * Shorthand for adding a single key listener
14730      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14731      * following options:
14732      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14733      * @param {Function} fn The function to call
14734      * @param {Object} scope (optional) The scope of the function
14735      */
14736     on : function(key, fn, scope){
14737         var keyCode, shift, ctrl, alt;
14738         if(typeof key == "object" && !(key instanceof Array)){
14739             keyCode = key.key;
14740             shift = key.shift;
14741             ctrl = key.ctrl;
14742             alt = key.alt;
14743         }else{
14744             keyCode = key;
14745         }
14746         this.addBinding({
14747             key: keyCode,
14748             shift: shift,
14749             ctrl: ctrl,
14750             alt: alt,
14751             fn: fn,
14752             scope: scope
14753         })
14754     },
14755
14756     // private
14757     handleKeyDown : function(e){
14758             if(this.enabled){ //just in case
14759             var b = this.bindings;
14760             for(var i = 0, len = b.length; i < len; i++){
14761                 b[i].call(this, e);
14762             }
14763             }
14764         },
14765         
14766         /**
14767          * Returns true if this KeyMap is enabled
14768          * @return {Boolean} 
14769          */
14770         isEnabled : function(){
14771             return this.enabled;  
14772         },
14773         
14774         /**
14775          * Enables this KeyMap
14776          */
14777         enable: function(){
14778                 if(!this.enabled){
14779                     this.el.on(this.eventName, this.handleKeyDown, this);
14780                     this.enabled = true;
14781                 }
14782         },
14783
14784         /**
14785          * Disable this KeyMap
14786          */
14787         disable: function(){
14788                 if(this.enabled){
14789                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14790                     this.enabled = false;
14791                 }
14792         }
14793 };/*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803
14804  
14805 /**
14806  * @class Roo.util.TextMetrics
14807  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14808  * wide, in pixels, a given block of text will be.
14809  * @singleton
14810  */
14811 Roo.util.TextMetrics = function(){
14812     var shared;
14813     return {
14814         /**
14815          * Measures the size of the specified text
14816          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14817          * that can affect the size of the rendered text
14818          * @param {String} text The text to measure
14819          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14820          * in order to accurately measure the text height
14821          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14822          */
14823         measure : function(el, text, fixedWidth){
14824             if(!shared){
14825                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14826             }
14827             shared.bind(el);
14828             shared.setFixedWidth(fixedWidth || 'auto');
14829             return shared.getSize(text);
14830         },
14831
14832         /**
14833          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14834          * the overhead of multiple calls to initialize the style properties on each measurement.
14835          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14836          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14837          * in order to accurately measure the text height
14838          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14839          */
14840         createInstance : function(el, fixedWidth){
14841             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14842         }
14843     };
14844 }();
14845
14846  
14847
14848 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14849     var ml = new Roo.Element(document.createElement('div'));
14850     document.body.appendChild(ml.dom);
14851     ml.position('absolute');
14852     ml.setLeftTop(-1000, -1000);
14853     ml.hide();
14854
14855     if(fixedWidth){
14856         ml.setWidth(fixedWidth);
14857     }
14858      
14859     var instance = {
14860         /**
14861          * Returns the size of the specified text based on the internal element's style and width properties
14862          * @memberOf Roo.util.TextMetrics.Instance#
14863          * @param {String} text The text to measure
14864          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14865          */
14866         getSize : function(text){
14867             ml.update(text);
14868             var s = ml.getSize();
14869             ml.update('');
14870             return s;
14871         },
14872
14873         /**
14874          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14875          * that can affect the size of the rendered text
14876          * @memberOf Roo.util.TextMetrics.Instance#
14877          * @param {String/HTMLElement} el The element, dom node or id
14878          */
14879         bind : function(el){
14880             ml.setStyle(
14881                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14882             );
14883         },
14884
14885         /**
14886          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14887          * to set a fixed width in order to accurately measure the text height.
14888          * @memberOf Roo.util.TextMetrics.Instance#
14889          * @param {Number} width The width to set on the element
14890          */
14891         setFixedWidth : function(width){
14892             ml.setWidth(width);
14893         },
14894
14895         /**
14896          * Returns the measured width of the specified text
14897          * @memberOf Roo.util.TextMetrics.Instance#
14898          * @param {String} text The text to measure
14899          * @return {Number} width The width in pixels
14900          */
14901         getWidth : function(text){
14902             ml.dom.style.width = 'auto';
14903             return this.getSize(text).width;
14904         },
14905
14906         /**
14907          * Returns the measured height of the specified text.  For multiline text, be sure to call
14908          * {@link #setFixedWidth} if necessary.
14909          * @memberOf Roo.util.TextMetrics.Instance#
14910          * @param {String} text The text to measure
14911          * @return {Number} height The height in pixels
14912          */
14913         getHeight : function(text){
14914             return this.getSize(text).height;
14915         }
14916     };
14917
14918     instance.bind(bindTo);
14919
14920     return instance;
14921 };
14922
14923 // backwards compat
14924 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14925  * Based on:
14926  * Ext JS Library 1.1.1
14927  * Copyright(c) 2006-2007, Ext JS, LLC.
14928  *
14929  * Originally Released Under LGPL - original licence link has changed is not relivant.
14930  *
14931  * Fork - LGPL
14932  * <script type="text/javascript">
14933  */
14934
14935 /**
14936  * @class Roo.state.Provider
14937  * Abstract base class for state provider implementations. This class provides methods
14938  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14939  * Provider interface.
14940  */
14941 Roo.state.Provider = function(){
14942     /**
14943      * @event statechange
14944      * Fires when a state change occurs.
14945      * @param {Provider} this This state provider
14946      * @param {String} key The state key which was changed
14947      * @param {String} value The encoded value for the state
14948      */
14949     this.addEvents({
14950         "statechange": true
14951     });
14952     this.state = {};
14953     Roo.state.Provider.superclass.constructor.call(this);
14954 };
14955 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14956     /**
14957      * Returns the current value for a key
14958      * @param {String} name The key name
14959      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14960      * @return {Mixed} The state data
14961      */
14962     get : function(name, defaultValue){
14963         return typeof this.state[name] == "undefined" ?
14964             defaultValue : this.state[name];
14965     },
14966     
14967     /**
14968      * Clears a value from the state
14969      * @param {String} name The key name
14970      */
14971     clear : function(name){
14972         delete this.state[name];
14973         this.fireEvent("statechange", this, name, null);
14974     },
14975     
14976     /**
14977      * Sets the value for a key
14978      * @param {String} name The key name
14979      * @param {Mixed} value The value to set
14980      */
14981     set : function(name, value){
14982         this.state[name] = value;
14983         this.fireEvent("statechange", this, name, value);
14984     },
14985     
14986     /**
14987      * Decodes a string previously encoded with {@link #encodeValue}.
14988      * @param {String} value The value to decode
14989      * @return {Mixed} The decoded value
14990      */
14991     decodeValue : function(cookie){
14992         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14993         var matches = re.exec(unescape(cookie));
14994         if(!matches || !matches[1]) {
14995             return; // non state cookie
14996         }
14997         var type = matches[1];
14998         var v = matches[2];
14999         switch(type){
15000             case "n":
15001                 return parseFloat(v);
15002             case "d":
15003                 return new Date(Date.parse(v));
15004             case "b":
15005                 return (v == "1");
15006             case "a":
15007                 var all = [];
15008                 var values = v.split("^");
15009                 for(var i = 0, len = values.length; i < len; i++){
15010                     all.push(this.decodeValue(values[i]));
15011                 }
15012                 return all;
15013            case "o":
15014                 var all = {};
15015                 var values = v.split("^");
15016                 for(var i = 0, len = values.length; i < len; i++){
15017                     var kv = values[i].split("=");
15018                     all[kv[0]] = this.decodeValue(kv[1]);
15019                 }
15020                 return all;
15021            default:
15022                 return v;
15023         }
15024     },
15025     
15026     /**
15027      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15028      * @param {Mixed} value The value to encode
15029      * @return {String} The encoded value
15030      */
15031     encodeValue : function(v){
15032         var enc;
15033         if(typeof v == "number"){
15034             enc = "n:" + v;
15035         }else if(typeof v == "boolean"){
15036             enc = "b:" + (v ? "1" : "0");
15037         }else if(v instanceof Date){
15038             enc = "d:" + v.toGMTString();
15039         }else if(v instanceof Array){
15040             var flat = "";
15041             for(var i = 0, len = v.length; i < len; i++){
15042                 flat += this.encodeValue(v[i]);
15043                 if(i != len-1) {
15044                     flat += "^";
15045                 }
15046             }
15047             enc = "a:" + flat;
15048         }else if(typeof v == "object"){
15049             var flat = "";
15050             for(var key in v){
15051                 if(typeof v[key] != "function"){
15052                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15053                 }
15054             }
15055             enc = "o:" + flat.substring(0, flat.length-1);
15056         }else{
15057             enc = "s:" + v;
15058         }
15059         return escape(enc);        
15060     }
15061 });
15062
15063 /*
15064  * Based on:
15065  * Ext JS Library 1.1.1
15066  * Copyright(c) 2006-2007, Ext JS, LLC.
15067  *
15068  * Originally Released Under LGPL - original licence link has changed is not relivant.
15069  *
15070  * Fork - LGPL
15071  * <script type="text/javascript">
15072  */
15073 /**
15074  * @class Roo.state.Manager
15075  * This is the global state manager. By default all components that are "state aware" check this class
15076  * for state information if you don't pass them a custom state provider. In order for this class
15077  * to be useful, it must be initialized with a provider when your application initializes.
15078  <pre><code>
15079 // in your initialization function
15080 init : function(){
15081    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15082    ...
15083    // supposed you have a {@link Roo.BorderLayout}
15084    var layout = new Roo.BorderLayout(...);
15085    layout.restoreState();
15086    // or a {Roo.BasicDialog}
15087    var dialog = new Roo.BasicDialog(...);
15088    dialog.restoreState();
15089  </code></pre>
15090  * @singleton
15091  */
15092 Roo.state.Manager = function(){
15093     var provider = new Roo.state.Provider();
15094     
15095     return {
15096         /**
15097          * Configures the default state provider for your application
15098          * @param {Provider} stateProvider The state provider to set
15099          */
15100         setProvider : function(stateProvider){
15101             provider = stateProvider;
15102         },
15103         
15104         /**
15105          * Returns the current value for a key
15106          * @param {String} name The key name
15107          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15108          * @return {Mixed} The state data
15109          */
15110         get : function(key, defaultValue){
15111             return provider.get(key, defaultValue);
15112         },
15113         
15114         /**
15115          * Sets the value for a key
15116          * @param {String} name The key name
15117          * @param {Mixed} value The state data
15118          */
15119          set : function(key, value){
15120             provider.set(key, value);
15121         },
15122         
15123         /**
15124          * Clears a value from the state
15125          * @param {String} name The key name
15126          */
15127         clear : function(key){
15128             provider.clear(key);
15129         },
15130         
15131         /**
15132          * Gets the currently configured state provider
15133          * @return {Provider} The state provider
15134          */
15135         getProvider : function(){
15136             return provider;
15137         }
15138     };
15139 }();
15140 /*
15141  * Based on:
15142  * Ext JS Library 1.1.1
15143  * Copyright(c) 2006-2007, Ext JS, LLC.
15144  *
15145  * Originally Released Under LGPL - original licence link has changed is not relivant.
15146  *
15147  * Fork - LGPL
15148  * <script type="text/javascript">
15149  */
15150 /**
15151  * @class Roo.state.CookieProvider
15152  * @extends Roo.state.Provider
15153  * The default Provider implementation which saves state via cookies.
15154  * <br />Usage:
15155  <pre><code>
15156    var cp = new Roo.state.CookieProvider({
15157        path: "/cgi-bin/",
15158        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15159        domain: "roojs.com"
15160    })
15161    Roo.state.Manager.setProvider(cp);
15162  </code></pre>
15163  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15164  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15165  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15166  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15167  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15168  * domain the page is running on including the 'www' like 'www.roojs.com')
15169  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15170  * @constructor
15171  * Create a new CookieProvider
15172  * @param {Object} config The configuration object
15173  */
15174 Roo.state.CookieProvider = function(config){
15175     Roo.state.CookieProvider.superclass.constructor.call(this);
15176     this.path = "/";
15177     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15178     this.domain = null;
15179     this.secure = false;
15180     Roo.apply(this, config);
15181     this.state = this.readCookies();
15182 };
15183
15184 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15185     // private
15186     set : function(name, value){
15187         if(typeof value == "undefined" || value === null){
15188             this.clear(name);
15189             return;
15190         }
15191         this.setCookie(name, value);
15192         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15193     },
15194
15195     // private
15196     clear : function(name){
15197         this.clearCookie(name);
15198         Roo.state.CookieProvider.superclass.clear.call(this, name);
15199     },
15200
15201     // private
15202     readCookies : function(){
15203         var cookies = {};
15204         var c = document.cookie + ";";
15205         var re = /\s?(.*?)=(.*?);/g;
15206         var matches;
15207         while((matches = re.exec(c)) != null){
15208             var name = matches[1];
15209             var value = matches[2];
15210             if(name && name.substring(0,3) == "ys-"){
15211                 cookies[name.substr(3)] = this.decodeValue(value);
15212             }
15213         }
15214         return cookies;
15215     },
15216
15217     // private
15218     setCookie : function(name, value){
15219         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15220            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15221            ((this.path == null) ? "" : ("; path=" + this.path)) +
15222            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15223            ((this.secure == true) ? "; secure" : "");
15224     },
15225
15226     // private
15227     clearCookie : function(name){
15228         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15229            ((this.path == null) ? "" : ("; path=" + this.path)) +
15230            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15231            ((this.secure == true) ? "; secure" : "");
15232     }
15233 });/*
15234  * Based on:
15235  * Ext JS Library 1.1.1
15236  * Copyright(c) 2006-2007, Ext JS, LLC.
15237  *
15238  * Originally Released Under LGPL - original licence link has changed is not relivant.
15239  *
15240  * Fork - LGPL
15241  * <script type="text/javascript">
15242  */
15243  
15244
15245 /**
15246  * @class Roo.ComponentMgr
15247  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15248  * @singleton
15249  */
15250 Roo.ComponentMgr = function(){
15251     var all = new Roo.util.MixedCollection();
15252
15253     return {
15254         /**
15255          * Registers a component.
15256          * @param {Roo.Component} c The component
15257          */
15258         register : function(c){
15259             all.add(c);
15260         },
15261
15262         /**
15263          * Unregisters a component.
15264          * @param {Roo.Component} c The component
15265          */
15266         unregister : function(c){
15267             all.remove(c);
15268         },
15269
15270         /**
15271          * Returns a component by id
15272          * @param {String} id The component id
15273          */
15274         get : function(id){
15275             return all.get(id);
15276         },
15277
15278         /**
15279          * Registers a function that will be called when a specified component is added to ComponentMgr
15280          * @param {String} id The component id
15281          * @param {Funtction} fn The callback function
15282          * @param {Object} scope The scope of the callback
15283          */
15284         onAvailable : function(id, fn, scope){
15285             all.on("add", function(index, o){
15286                 if(o.id == id){
15287                     fn.call(scope || o, o);
15288                     all.un("add", fn, scope);
15289                 }
15290             });
15291         }
15292     };
15293 }();/*
15294  * Based on:
15295  * Ext JS Library 1.1.1
15296  * Copyright(c) 2006-2007, Ext JS, LLC.
15297  *
15298  * Originally Released Under LGPL - original licence link has changed is not relivant.
15299  *
15300  * Fork - LGPL
15301  * <script type="text/javascript">
15302  */
15303  
15304 /**
15305  * @class Roo.Component
15306  * @extends Roo.util.Observable
15307  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15308  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15309  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15310  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15311  * All visual components (widgets) that require rendering into a layout should subclass Component.
15312  * @constructor
15313  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15314  * 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
15315  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15316  */
15317 Roo.Component = function(config){
15318     config = config || {};
15319     if(config.tagName || config.dom || typeof config == "string"){ // element object
15320         config = {el: config, id: config.id || config};
15321     }
15322     this.initialConfig = config;
15323
15324     Roo.apply(this, config);
15325     this.addEvents({
15326         /**
15327          * @event disable
15328          * Fires after the component is disabled.
15329              * @param {Roo.Component} this
15330              */
15331         disable : true,
15332         /**
15333          * @event enable
15334          * Fires after the component is enabled.
15335              * @param {Roo.Component} this
15336              */
15337         enable : true,
15338         /**
15339          * @event beforeshow
15340          * Fires before the component is shown.  Return false to stop the show.
15341              * @param {Roo.Component} this
15342              */
15343         beforeshow : true,
15344         /**
15345          * @event show
15346          * Fires after the component is shown.
15347              * @param {Roo.Component} this
15348              */
15349         show : true,
15350         /**
15351          * @event beforehide
15352          * Fires before the component is hidden. Return false to stop the hide.
15353              * @param {Roo.Component} this
15354              */
15355         beforehide : true,
15356         /**
15357          * @event hide
15358          * Fires after the component is hidden.
15359              * @param {Roo.Component} this
15360              */
15361         hide : true,
15362         /**
15363          * @event beforerender
15364          * Fires before the component is rendered. Return false to stop the render.
15365              * @param {Roo.Component} this
15366              */
15367         beforerender : true,
15368         /**
15369          * @event render
15370          * Fires after the component is rendered.
15371              * @param {Roo.Component} this
15372              */
15373         render : true,
15374         /**
15375          * @event beforedestroy
15376          * Fires before the component is destroyed. Return false to stop the destroy.
15377              * @param {Roo.Component} this
15378              */
15379         beforedestroy : true,
15380         /**
15381          * @event destroy
15382          * Fires after the component is destroyed.
15383              * @param {Roo.Component} this
15384              */
15385         destroy : true
15386     });
15387     if(!this.id){
15388         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15389     }
15390     Roo.ComponentMgr.register(this);
15391     Roo.Component.superclass.constructor.call(this);
15392     this.initComponent();
15393     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15394         this.render(this.renderTo);
15395         delete this.renderTo;
15396     }
15397 };
15398
15399 /** @private */
15400 Roo.Component.AUTO_ID = 1000;
15401
15402 Roo.extend(Roo.Component, Roo.util.Observable, {
15403     /**
15404      * @scope Roo.Component.prototype
15405      * @type {Boolean}
15406      * true if this component is hidden. Read-only.
15407      */
15408     hidden : false,
15409     /**
15410      * @type {Boolean}
15411      * true if this component is disabled. Read-only.
15412      */
15413     disabled : false,
15414     /**
15415      * @type {Boolean}
15416      * true if this component has been rendered. Read-only.
15417      */
15418     rendered : false,
15419     
15420     /** @cfg {String} disableClass
15421      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15422      */
15423     disabledClass : "x-item-disabled",
15424         /** @cfg {Boolean} allowDomMove
15425          * Whether the component can move the Dom node when rendering (defaults to true).
15426          */
15427     allowDomMove : true,
15428     /** @cfg {String} hideMode (display|visibility)
15429      * How this component should hidden. Supported values are
15430      * "visibility" (css visibility), "offsets" (negative offset position) and
15431      * "display" (css display) - defaults to "display".
15432      */
15433     hideMode: 'display',
15434
15435     /** @private */
15436     ctype : "Roo.Component",
15437
15438     /**
15439      * @cfg {String} actionMode 
15440      * which property holds the element that used for  hide() / show() / disable() / enable()
15441      * default is 'el' 
15442      */
15443     actionMode : "el",
15444
15445     /** @private */
15446     getActionEl : function(){
15447         return this[this.actionMode];
15448     },
15449
15450     initComponent : Roo.emptyFn,
15451     /**
15452      * If this is a lazy rendering component, render it to its container element.
15453      * @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.
15454      */
15455     render : function(container, position){
15456         
15457         if(this.rendered){
15458             return this;
15459         }
15460         
15461         if(this.fireEvent("beforerender", this) === false){
15462             return false;
15463         }
15464         
15465         if(!container && this.el){
15466             this.el = Roo.get(this.el);
15467             container = this.el.dom.parentNode;
15468             this.allowDomMove = false;
15469         }
15470         this.container = Roo.get(container);
15471         this.rendered = true;
15472         if(position !== undefined){
15473             if(typeof position == 'number'){
15474                 position = this.container.dom.childNodes[position];
15475             }else{
15476                 position = Roo.getDom(position);
15477             }
15478         }
15479         this.onRender(this.container, position || null);
15480         if(this.cls){
15481             this.el.addClass(this.cls);
15482             delete this.cls;
15483         }
15484         if(this.style){
15485             this.el.applyStyles(this.style);
15486             delete this.style;
15487         }
15488         this.fireEvent("render", this);
15489         this.afterRender(this.container);
15490         if(this.hidden){
15491             this.hide();
15492         }
15493         if(this.disabled){
15494             this.disable();
15495         }
15496
15497         return this;
15498         
15499     },
15500
15501     /** @private */
15502     // default function is not really useful
15503     onRender : function(ct, position){
15504         if(this.el){
15505             this.el = Roo.get(this.el);
15506             if(this.allowDomMove !== false){
15507                 ct.dom.insertBefore(this.el.dom, position);
15508             }
15509         }
15510     },
15511
15512     /** @private */
15513     getAutoCreate : function(){
15514         var cfg = typeof this.autoCreate == "object" ?
15515                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15516         if(this.id && !cfg.id){
15517             cfg.id = this.id;
15518         }
15519         return cfg;
15520     },
15521
15522     /** @private */
15523     afterRender : Roo.emptyFn,
15524
15525     /**
15526      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15527      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15528      */
15529     destroy : function(){
15530         if(this.fireEvent("beforedestroy", this) !== false){
15531             this.purgeListeners();
15532             this.beforeDestroy();
15533             if(this.rendered){
15534                 this.el.removeAllListeners();
15535                 this.el.remove();
15536                 if(this.actionMode == "container"){
15537                     this.container.remove();
15538                 }
15539             }
15540             this.onDestroy();
15541             Roo.ComponentMgr.unregister(this);
15542             this.fireEvent("destroy", this);
15543         }
15544     },
15545
15546         /** @private */
15547     beforeDestroy : function(){
15548
15549     },
15550
15551         /** @private */
15552         onDestroy : function(){
15553
15554     },
15555
15556     /**
15557      * Returns the underlying {@link Roo.Element}.
15558      * @return {Roo.Element} The element
15559      */
15560     getEl : function(){
15561         return this.el;
15562     },
15563
15564     /**
15565      * Returns the id of this component.
15566      * @return {String}
15567      */
15568     getId : function(){
15569         return this.id;
15570     },
15571
15572     /**
15573      * Try to focus this component.
15574      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15575      * @return {Roo.Component} this
15576      */
15577     focus : function(selectText){
15578         if(this.rendered){
15579             this.el.focus();
15580             if(selectText === true){
15581                 this.el.dom.select();
15582             }
15583         }
15584         return this;
15585     },
15586
15587     /** @private */
15588     blur : function(){
15589         if(this.rendered){
15590             this.el.blur();
15591         }
15592         return this;
15593     },
15594
15595     /**
15596      * Disable this component.
15597      * @return {Roo.Component} this
15598      */
15599     disable : function(){
15600         if(this.rendered){
15601             this.onDisable();
15602         }
15603         this.disabled = true;
15604         this.fireEvent("disable", this);
15605         return this;
15606     },
15607
15608         // private
15609     onDisable : function(){
15610         this.getActionEl().addClass(this.disabledClass);
15611         this.el.dom.disabled = true;
15612     },
15613
15614     /**
15615      * Enable this component.
15616      * @return {Roo.Component} this
15617      */
15618     enable : function(){
15619         if(this.rendered){
15620             this.onEnable();
15621         }
15622         this.disabled = false;
15623         this.fireEvent("enable", this);
15624         return this;
15625     },
15626
15627         // private
15628     onEnable : function(){
15629         this.getActionEl().removeClass(this.disabledClass);
15630         this.el.dom.disabled = false;
15631     },
15632
15633     /**
15634      * Convenience function for setting disabled/enabled by boolean.
15635      * @param {Boolean} disabled
15636      */
15637     setDisabled : function(disabled){
15638         this[disabled ? "disable" : "enable"]();
15639     },
15640
15641     /**
15642      * Show this component.
15643      * @return {Roo.Component} this
15644      */
15645     show: function(){
15646         if(this.fireEvent("beforeshow", this) !== false){
15647             this.hidden = false;
15648             if(this.rendered){
15649                 this.onShow();
15650             }
15651             this.fireEvent("show", this);
15652         }
15653         return this;
15654     },
15655
15656     // private
15657     onShow : function(){
15658         var ae = this.getActionEl();
15659         if(this.hideMode == 'visibility'){
15660             ae.dom.style.visibility = "visible";
15661         }else if(this.hideMode == 'offsets'){
15662             ae.removeClass('x-hidden');
15663         }else{
15664             ae.dom.style.display = "";
15665         }
15666     },
15667
15668     /**
15669      * Hide this component.
15670      * @return {Roo.Component} this
15671      */
15672     hide: function(){
15673         if(this.fireEvent("beforehide", this) !== false){
15674             this.hidden = true;
15675             if(this.rendered){
15676                 this.onHide();
15677             }
15678             this.fireEvent("hide", this);
15679         }
15680         return this;
15681     },
15682
15683     // private
15684     onHide : function(){
15685         var ae = this.getActionEl();
15686         if(this.hideMode == 'visibility'){
15687             ae.dom.style.visibility = "hidden";
15688         }else if(this.hideMode == 'offsets'){
15689             ae.addClass('x-hidden');
15690         }else{
15691             ae.dom.style.display = "none";
15692         }
15693     },
15694
15695     /**
15696      * Convenience function to hide or show this component by boolean.
15697      * @param {Boolean} visible True to show, false to hide
15698      * @return {Roo.Component} this
15699      */
15700     setVisible: function(visible){
15701         if(visible) {
15702             this.show();
15703         }else{
15704             this.hide();
15705         }
15706         return this;
15707     },
15708
15709     /**
15710      * Returns true if this component is visible.
15711      */
15712     isVisible : function(){
15713         return this.getActionEl().isVisible();
15714     },
15715
15716     cloneConfig : function(overrides){
15717         overrides = overrides || {};
15718         var id = overrides.id || Roo.id();
15719         var cfg = Roo.applyIf(overrides, this.initialConfig);
15720         cfg.id = id; // prevent dup id
15721         return new this.constructor(cfg);
15722     }
15723 });/*
15724  * Based on:
15725  * Ext JS Library 1.1.1
15726  * Copyright(c) 2006-2007, Ext JS, LLC.
15727  *
15728  * Originally Released Under LGPL - original licence link has changed is not relivant.
15729  *
15730  * Fork - LGPL
15731  * <script type="text/javascript">
15732  */
15733
15734 /**
15735  * @class Roo.BoxComponent
15736  * @extends Roo.Component
15737  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15738  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15739  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15740  * layout containers.
15741  * @constructor
15742  * @param {Roo.Element/String/Object} config The configuration options.
15743  */
15744 Roo.BoxComponent = function(config){
15745     Roo.Component.call(this, config);
15746     this.addEvents({
15747         /**
15748          * @event resize
15749          * Fires after the component is resized.
15750              * @param {Roo.Component} this
15751              * @param {Number} adjWidth The box-adjusted width that was set
15752              * @param {Number} adjHeight The box-adjusted height that was set
15753              * @param {Number} rawWidth The width that was originally specified
15754              * @param {Number} rawHeight The height that was originally specified
15755              */
15756         resize : true,
15757         /**
15758          * @event move
15759          * Fires after the component is moved.
15760              * @param {Roo.Component} this
15761              * @param {Number} x The new x position
15762              * @param {Number} y The new y position
15763              */
15764         move : true
15765     });
15766 };
15767
15768 Roo.extend(Roo.BoxComponent, Roo.Component, {
15769     // private, set in afterRender to signify that the component has been rendered
15770     boxReady : false,
15771     // private, used to defer height settings to subclasses
15772     deferHeight: false,
15773     /** @cfg {Number} width
15774      * width (optional) size of component
15775      */
15776      /** @cfg {Number} height
15777      * height (optional) size of component
15778      */
15779      
15780     /**
15781      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15782      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15783      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15784      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15785      * @return {Roo.BoxComponent} this
15786      */
15787     setSize : function(w, h){
15788         // support for standard size objects
15789         if(typeof w == 'object'){
15790             h = w.height;
15791             w = w.width;
15792         }
15793         // not rendered
15794         if(!this.boxReady){
15795             this.width = w;
15796             this.height = h;
15797             return this;
15798         }
15799
15800         // prevent recalcs when not needed
15801         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15802             return this;
15803         }
15804         this.lastSize = {width: w, height: h};
15805
15806         var adj = this.adjustSize(w, h);
15807         var aw = adj.width, ah = adj.height;
15808         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15809             var rz = this.getResizeEl();
15810             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15811                 rz.setSize(aw, ah);
15812             }else if(!this.deferHeight && ah !== undefined){
15813                 rz.setHeight(ah);
15814             }else if(aw !== undefined){
15815                 rz.setWidth(aw);
15816             }
15817             this.onResize(aw, ah, w, h);
15818             this.fireEvent('resize', this, aw, ah, w, h);
15819         }
15820         return this;
15821     },
15822
15823     /**
15824      * Gets the current size of the component's underlying element.
15825      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15826      */
15827     getSize : function(){
15828         return this.el.getSize();
15829     },
15830
15831     /**
15832      * Gets the current XY position of the component's underlying element.
15833      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15834      * @return {Array} The XY position of the element (e.g., [100, 200])
15835      */
15836     getPosition : function(local){
15837         if(local === true){
15838             return [this.el.getLeft(true), this.el.getTop(true)];
15839         }
15840         return this.xy || this.el.getXY();
15841     },
15842
15843     /**
15844      * Gets the current box measurements of the component's underlying element.
15845      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15846      * @returns {Object} box An object in the format {x, y, width, height}
15847      */
15848     getBox : function(local){
15849         var s = this.el.getSize();
15850         if(local){
15851             s.x = this.el.getLeft(true);
15852             s.y = this.el.getTop(true);
15853         }else{
15854             var xy = this.xy || this.el.getXY();
15855             s.x = xy[0];
15856             s.y = xy[1];
15857         }
15858         return s;
15859     },
15860
15861     /**
15862      * Sets the current box measurements of the component's underlying element.
15863      * @param {Object} box An object in the format {x, y, width, height}
15864      * @returns {Roo.BoxComponent} this
15865      */
15866     updateBox : function(box){
15867         this.setSize(box.width, box.height);
15868         this.setPagePosition(box.x, box.y);
15869         return this;
15870     },
15871
15872     // protected
15873     getResizeEl : function(){
15874         return this.resizeEl || this.el;
15875     },
15876
15877     // protected
15878     getPositionEl : function(){
15879         return this.positionEl || this.el;
15880     },
15881
15882     /**
15883      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15884      * This method fires the move event.
15885      * @param {Number} left The new left
15886      * @param {Number} top The new top
15887      * @returns {Roo.BoxComponent} this
15888      */
15889     setPosition : function(x, y){
15890         this.x = x;
15891         this.y = y;
15892         if(!this.boxReady){
15893             return this;
15894         }
15895         var adj = this.adjustPosition(x, y);
15896         var ax = adj.x, ay = adj.y;
15897
15898         var el = this.getPositionEl();
15899         if(ax !== undefined || ay !== undefined){
15900             if(ax !== undefined && ay !== undefined){
15901                 el.setLeftTop(ax, ay);
15902             }else if(ax !== undefined){
15903                 el.setLeft(ax);
15904             }else if(ay !== undefined){
15905                 el.setTop(ay);
15906             }
15907             this.onPosition(ax, ay);
15908             this.fireEvent('move', this, ax, ay);
15909         }
15910         return this;
15911     },
15912
15913     /**
15914      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15915      * This method fires the move event.
15916      * @param {Number} x The new x position
15917      * @param {Number} y The new y position
15918      * @returns {Roo.BoxComponent} this
15919      */
15920     setPagePosition : function(x, y){
15921         this.pageX = x;
15922         this.pageY = y;
15923         if(!this.boxReady){
15924             return;
15925         }
15926         if(x === undefined || y === undefined){ // cannot translate undefined points
15927             return;
15928         }
15929         var p = this.el.translatePoints(x, y);
15930         this.setPosition(p.left, p.top);
15931         return this;
15932     },
15933
15934     // private
15935     onRender : function(ct, position){
15936         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15937         if(this.resizeEl){
15938             this.resizeEl = Roo.get(this.resizeEl);
15939         }
15940         if(this.positionEl){
15941             this.positionEl = Roo.get(this.positionEl);
15942         }
15943     },
15944
15945     // private
15946     afterRender : function(){
15947         Roo.BoxComponent.superclass.afterRender.call(this);
15948         this.boxReady = true;
15949         this.setSize(this.width, this.height);
15950         if(this.x || this.y){
15951             this.setPosition(this.x, this.y);
15952         }
15953         if(this.pageX || this.pageY){
15954             this.setPagePosition(this.pageX, this.pageY);
15955         }
15956     },
15957
15958     /**
15959      * Force the component's size to recalculate based on the underlying element's current height and width.
15960      * @returns {Roo.BoxComponent} this
15961      */
15962     syncSize : function(){
15963         delete this.lastSize;
15964         this.setSize(this.el.getWidth(), this.el.getHeight());
15965         return this;
15966     },
15967
15968     /**
15969      * Called after the component is resized, this method is empty by default but can be implemented by any
15970      * subclass that needs to perform custom logic after a resize occurs.
15971      * @param {Number} adjWidth The box-adjusted width that was set
15972      * @param {Number} adjHeight The box-adjusted height that was set
15973      * @param {Number} rawWidth The width that was originally specified
15974      * @param {Number} rawHeight The height that was originally specified
15975      */
15976     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15977
15978     },
15979
15980     /**
15981      * Called after the component is moved, this method is empty by default but can be implemented by any
15982      * subclass that needs to perform custom logic after a move occurs.
15983      * @param {Number} x The new x position
15984      * @param {Number} y The new y position
15985      */
15986     onPosition : function(x, y){
15987
15988     },
15989
15990     // private
15991     adjustSize : function(w, h){
15992         if(this.autoWidth){
15993             w = 'auto';
15994         }
15995         if(this.autoHeight){
15996             h = 'auto';
15997         }
15998         return {width : w, height: h};
15999     },
16000
16001     // private
16002     adjustPosition : function(x, y){
16003         return {x : x, y: y};
16004     }
16005 });/*
16006  * Original code for Roojs - LGPL
16007  * <script type="text/javascript">
16008  */
16009  
16010 /**
16011  * @class Roo.XComponent
16012  * A delayed Element creator...
16013  * Or a way to group chunks of interface together.
16014  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16015  *  used in conjunction with XComponent.build() it will create an instance of each element,
16016  *  then call addxtype() to build the User interface.
16017  * 
16018  * Mypart.xyx = new Roo.XComponent({
16019
16020     parent : 'Mypart.xyz', // empty == document.element.!!
16021     order : '001',
16022     name : 'xxxx'
16023     region : 'xxxx'
16024     disabled : function() {} 
16025      
16026     tree : function() { // return an tree of xtype declared components
16027         var MODULE = this;
16028         return 
16029         {
16030             xtype : 'NestedLayoutPanel',
16031             // technicall
16032         }
16033      ]
16034  *})
16035  *
16036  *
16037  * It can be used to build a big heiracy, with parent etc.
16038  * or you can just use this to render a single compoent to a dom element
16039  * MYPART.render(Roo.Element | String(id) | dom_element )
16040  *
16041  *
16042  * Usage patterns.
16043  *
16044  * Classic Roo
16045  *
16046  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16047  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16048  *
16049  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16050  *
16051  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16052  * - if mulitple topModules exist, the last one is defined as the top module.
16053  *
16054  * Embeded Roo
16055  * 
16056  * When the top level or multiple modules are to embedded into a existing HTML page,
16057  * the parent element can container '#id' of the element where the module will be drawn.
16058  *
16059  * Bootstrap Roo
16060  *
16061  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16062  * it relies more on a include mechanism, where sub modules are included into an outer page.
16063  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16064  * 
16065  * Bootstrap Roo Included elements
16066  *
16067  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16068  * hence confusing the component builder as it thinks there are multiple top level elements. 
16069  *
16070  * String Over-ride & Translations
16071  *
16072  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16073  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16074  * are needed. @see Roo.XComponent.overlayString  
16075  * 
16076  * 
16077  * 
16078  * @extends Roo.util.Observable
16079  * @constructor
16080  * @param cfg {Object} configuration of component
16081  * 
16082  */
16083 Roo.XComponent = function(cfg) {
16084     Roo.apply(this, cfg);
16085     this.addEvents({ 
16086         /**
16087              * @event built
16088              * Fires when this the componnt is built
16089              * @param {Roo.XComponent} c the component
16090              */
16091         'built' : true
16092         
16093     });
16094     this.region = this.region || 'center'; // default..
16095     Roo.XComponent.register(this);
16096     this.modules = false;
16097     this.el = false; // where the layout goes..
16098     
16099     
16100 }
16101 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16102     /**
16103      * @property el
16104      * The created element (with Roo.factory())
16105      * @type {Roo.Layout}
16106      */
16107     el  : false,
16108     
16109     /**
16110      * @property el
16111      * for BC  - use el in new code
16112      * @type {Roo.Layout}
16113      */
16114     panel : false,
16115     
16116     /**
16117      * @property layout
16118      * for BC  - use el in new code
16119      * @type {Roo.Layout}
16120      */
16121     layout : false,
16122     
16123      /**
16124      * @cfg {Function|boolean} disabled
16125      * If this module is disabled by some rule, return true from the funtion
16126      */
16127     disabled : false,
16128     
16129     /**
16130      * @cfg {String} parent 
16131      * Name of parent element which it get xtype added to..
16132      */
16133     parent: false,
16134     
16135     /**
16136      * @cfg {String} order
16137      * Used to set the order in which elements are created (usefull for multiple tabs)
16138      */
16139     
16140     order : false,
16141     /**
16142      * @cfg {String} name
16143      * String to display while loading.
16144      */
16145     name : false,
16146     /**
16147      * @cfg {String} region
16148      * Region to render component to (defaults to center)
16149      */
16150     region : 'center',
16151     
16152     /**
16153      * @cfg {Array} items
16154      * A single item array - the first element is the root of the tree..
16155      * It's done this way to stay compatible with the Xtype system...
16156      */
16157     items : false,
16158     
16159     /**
16160      * @property _tree
16161      * The method that retuns the tree of parts that make up this compoennt 
16162      * @type {function}
16163      */
16164     _tree  : false,
16165     
16166      /**
16167      * render
16168      * render element to dom or tree
16169      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16170      */
16171     
16172     render : function(el)
16173     {
16174         
16175         el = el || false;
16176         var hp = this.parent ? 1 : 0;
16177         Roo.debug &&  Roo.log(this);
16178         
16179         var tree = this._tree ? this._tree() : this.tree();
16180
16181         
16182         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16183             // if parent is a '#.....' string, then let's use that..
16184             var ename = this.parent.substr(1);
16185             this.parent = false;
16186             Roo.debug && Roo.log(ename);
16187             switch (ename) {
16188                 case 'bootstrap-body':
16189                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16190                         // this is the BorderLayout standard?
16191                        this.parent = { el : true };
16192                        break;
16193                     }
16194                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16195                         // need to insert stuff...
16196                         this.parent =  {
16197                              el : new Roo.bootstrap.layout.Border({
16198                                  el : document.body, 
16199                      
16200                                  center: {
16201                                     titlebar: false,
16202                                     autoScroll:false,
16203                                     closeOnTab: true,
16204                                     tabPosition: 'top',
16205                                       //resizeTabs: true,
16206                                     alwaysShowTabs: true,
16207                                     hideTabs: false
16208                                      //minTabWidth: 140
16209                                  }
16210                              })
16211                         
16212                          };
16213                          break;
16214                     }
16215                          
16216                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16217                         this.parent = { el :  new  Roo.bootstrap.Body() };
16218                         Roo.debug && Roo.log("setting el to doc body");
16219                          
16220                     } else {
16221                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16222                     }
16223                     break;
16224                 case 'bootstrap':
16225                     this.parent = { el : true};
16226                     // fall through
16227                 default:
16228                     el = Roo.get(ename);
16229                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16230                         this.parent = { el : true};
16231                     }
16232                     
16233                     break;
16234             }
16235                 
16236             
16237             if (!el && !this.parent) {
16238                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16239                 return;
16240             }
16241         }
16242         
16243         Roo.debug && Roo.log("EL:");
16244         Roo.debug && Roo.log(el);
16245         Roo.debug && Roo.log("this.parent.el:");
16246         Roo.debug && Roo.log(this.parent.el);
16247         
16248
16249         // altertive root elements ??? - we need a better way to indicate these.
16250         var is_alt = Roo.XComponent.is_alt ||
16251                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16252                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16253                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16254         
16255         
16256         
16257         if (!this.parent && is_alt) {
16258             //el = Roo.get(document.body);
16259             this.parent = { el : true };
16260         }
16261             
16262             
16263         
16264         if (!this.parent) {
16265             
16266             Roo.debug && Roo.log("no parent - creating one");
16267             
16268             el = el ? Roo.get(el) : false;      
16269             
16270             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16271                 
16272                 this.parent =  {
16273                     el : new Roo.bootstrap.layout.Border({
16274                         el: el || document.body,
16275                     
16276                         center: {
16277                             titlebar: false,
16278                             autoScroll:false,
16279                             closeOnTab: true,
16280                             tabPosition: 'top',
16281                              //resizeTabs: true,
16282                             alwaysShowTabs: false,
16283                             hideTabs: true,
16284                             minTabWidth: 140,
16285                             overflow: 'visible'
16286                          }
16287                      })
16288                 };
16289             } else {
16290             
16291                 // it's a top level one..
16292                 this.parent =  {
16293                     el : new Roo.BorderLayout(el || document.body, {
16294                         center: {
16295                             titlebar: false,
16296                             autoScroll:false,
16297                             closeOnTab: true,
16298                             tabPosition: 'top',
16299                              //resizeTabs: true,
16300                             alwaysShowTabs: el && hp? false :  true,
16301                             hideTabs: el || !hp ? true :  false,
16302                             minTabWidth: 140
16303                          }
16304                     })
16305                 };
16306             }
16307         }
16308         
16309         if (!this.parent.el) {
16310                 // probably an old style ctor, which has been disabled.
16311                 return;
16312
16313         }
16314                 // The 'tree' method is  '_tree now' 
16315             
16316         tree.region = tree.region || this.region;
16317         var is_body = false;
16318         if (this.parent.el === true) {
16319             // bootstrap... - body..
16320             if (el) {
16321                 tree.el = el;
16322             }
16323             this.parent.el = Roo.factory(tree);
16324             is_body = true;
16325         }
16326         
16327         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16328         this.fireEvent('built', this);
16329         
16330         this.panel = this.el;
16331         this.layout = this.panel.layout;
16332         this.parentLayout = this.parent.layout  || false;  
16333          
16334     }
16335     
16336 });
16337
16338 Roo.apply(Roo.XComponent, {
16339     /**
16340      * @property  hideProgress
16341      * true to disable the building progress bar.. usefull on single page renders.
16342      * @type Boolean
16343      */
16344     hideProgress : false,
16345     /**
16346      * @property  buildCompleted
16347      * True when the builder has completed building the interface.
16348      * @type Boolean
16349      */
16350     buildCompleted : false,
16351      
16352     /**
16353      * @property  topModule
16354      * the upper most module - uses document.element as it's constructor.
16355      * @type Object
16356      */
16357      
16358     topModule  : false,
16359       
16360     /**
16361      * @property  modules
16362      * array of modules to be created by registration system.
16363      * @type {Array} of Roo.XComponent
16364      */
16365     
16366     modules : [],
16367     /**
16368      * @property  elmodules
16369      * array of modules to be created by which use #ID 
16370      * @type {Array} of Roo.XComponent
16371      */
16372      
16373     elmodules : [],
16374
16375      /**
16376      * @property  is_alt
16377      * Is an alternative Root - normally used by bootstrap or other systems,
16378      *    where the top element in the tree can wrap 'body' 
16379      * @type {boolean}  (default false)
16380      */
16381      
16382     is_alt : false,
16383     /**
16384      * @property  build_from_html
16385      * Build elements from html - used by bootstrap HTML stuff 
16386      *    - this is cleared after build is completed
16387      * @type {boolean}    (default false)
16388      */
16389      
16390     build_from_html : false,
16391     /**
16392      * Register components to be built later.
16393      *
16394      * This solves the following issues
16395      * - Building is not done on page load, but after an authentication process has occured.
16396      * - Interface elements are registered on page load
16397      * - Parent Interface elements may not be loaded before child, so this handles that..
16398      * 
16399      *
16400      * example:
16401      * 
16402      * MyApp.register({
16403           order : '000001',
16404           module : 'Pman.Tab.projectMgr',
16405           region : 'center',
16406           parent : 'Pman.layout',
16407           disabled : false,  // or use a function..
16408         })
16409      
16410      * * @param {Object} details about module
16411      */
16412     register : function(obj) {
16413                 
16414         Roo.XComponent.event.fireEvent('register', obj);
16415         switch(typeof(obj.disabled) ) {
16416                 
16417             case 'undefined':
16418                 break;
16419             
16420             case 'function':
16421                 if ( obj.disabled() ) {
16422                         return;
16423                 }
16424                 break;
16425             
16426             default:
16427                 if (obj.disabled || obj.region == '#disabled') {
16428                         return;
16429                 }
16430                 break;
16431         }
16432                 
16433         this.modules.push(obj);
16434          
16435     },
16436     /**
16437      * convert a string to an object..
16438      * eg. 'AAA.BBB' -> finds AAA.BBB
16439
16440      */
16441     
16442     toObject : function(str)
16443     {
16444         if (!str || typeof(str) == 'object') {
16445             return str;
16446         }
16447         if (str.substring(0,1) == '#') {
16448             return str;
16449         }
16450
16451         var ar = str.split('.');
16452         var rt, o;
16453         rt = ar.shift();
16454             /** eval:var:o */
16455         try {
16456             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16457         } catch (e) {
16458             throw "Module not found : " + str;
16459         }
16460         
16461         if (o === false) {
16462             throw "Module not found : " + str;
16463         }
16464         Roo.each(ar, function(e) {
16465             if (typeof(o[e]) == 'undefined') {
16466                 throw "Module not found : " + str;
16467             }
16468             o = o[e];
16469         });
16470         
16471         return o;
16472         
16473     },
16474     
16475     
16476     /**
16477      * move modules into their correct place in the tree..
16478      * 
16479      */
16480     preBuild : function ()
16481     {
16482         var _t = this;
16483         Roo.each(this.modules , function (obj)
16484         {
16485             Roo.XComponent.event.fireEvent('beforebuild', obj);
16486             
16487             var opar = obj.parent;
16488             try { 
16489                 obj.parent = this.toObject(opar);
16490             } catch(e) {
16491                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16492                 return;
16493             }
16494             
16495             if (!obj.parent) {
16496                 Roo.debug && Roo.log("GOT top level module");
16497                 Roo.debug && Roo.log(obj);
16498                 obj.modules = new Roo.util.MixedCollection(false, 
16499                     function(o) { return o.order + '' }
16500                 );
16501                 this.topModule = obj;
16502                 return;
16503             }
16504                         // parent is a string (usually a dom element name..)
16505             if (typeof(obj.parent) == 'string') {
16506                 this.elmodules.push(obj);
16507                 return;
16508             }
16509             if (obj.parent.constructor != Roo.XComponent) {
16510                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16511             }
16512             if (!obj.parent.modules) {
16513                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16514                     function(o) { return o.order + '' }
16515                 );
16516             }
16517             if (obj.parent.disabled) {
16518                 obj.disabled = true;
16519             }
16520             obj.parent.modules.add(obj);
16521         }, this);
16522     },
16523     
16524      /**
16525      * make a list of modules to build.
16526      * @return {Array} list of modules. 
16527      */ 
16528     
16529     buildOrder : function()
16530     {
16531         var _this = this;
16532         var cmp = function(a,b) {   
16533             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16534         };
16535         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16536             throw "No top level modules to build";
16537         }
16538         
16539         // make a flat list in order of modules to build.
16540         var mods = this.topModule ? [ this.topModule ] : [];
16541                 
16542         
16543         // elmodules (is a list of DOM based modules )
16544         Roo.each(this.elmodules, function(e) {
16545             mods.push(e);
16546             if (!this.topModule &&
16547                 typeof(e.parent) == 'string' &&
16548                 e.parent.substring(0,1) == '#' &&
16549                 Roo.get(e.parent.substr(1))
16550                ) {
16551                 
16552                 _this.topModule = e;
16553             }
16554             
16555         });
16556
16557         
16558         // add modules to their parents..
16559         var addMod = function(m) {
16560             Roo.debug && Roo.log("build Order: add: " + m.name);
16561                 
16562             mods.push(m);
16563             if (m.modules && !m.disabled) {
16564                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16565                 m.modules.keySort('ASC',  cmp );
16566                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16567     
16568                 m.modules.each(addMod);
16569             } else {
16570                 Roo.debug && Roo.log("build Order: no child modules");
16571             }
16572             // not sure if this is used any more..
16573             if (m.finalize) {
16574                 m.finalize.name = m.name + " (clean up) ";
16575                 mods.push(m.finalize);
16576             }
16577             
16578         }
16579         if (this.topModule && this.topModule.modules) { 
16580             this.topModule.modules.keySort('ASC',  cmp );
16581             this.topModule.modules.each(addMod);
16582         } 
16583         return mods;
16584     },
16585     
16586      /**
16587      * Build the registered modules.
16588      * @param {Object} parent element.
16589      * @param {Function} optional method to call after module has been added.
16590      * 
16591      */ 
16592    
16593     build : function(opts) 
16594     {
16595         
16596         if (typeof(opts) != 'undefined') {
16597             Roo.apply(this,opts);
16598         }
16599         
16600         this.preBuild();
16601         var mods = this.buildOrder();
16602       
16603         //this.allmods = mods;
16604         //Roo.debug && Roo.log(mods);
16605         //return;
16606         if (!mods.length) { // should not happen
16607             throw "NO modules!!!";
16608         }
16609         
16610         
16611         var msg = "Building Interface...";
16612         // flash it up as modal - so we store the mask!?
16613         if (!this.hideProgress && Roo.MessageBox) {
16614             Roo.MessageBox.show({ title: 'loading' });
16615             Roo.MessageBox.show({
16616                title: "Please wait...",
16617                msg: msg,
16618                width:450,
16619                progress:true,
16620                buttons : false,
16621                closable:false,
16622                modal: false
16623               
16624             });
16625         }
16626         var total = mods.length;
16627         
16628         var _this = this;
16629         var progressRun = function() {
16630             if (!mods.length) {
16631                 Roo.debug && Roo.log('hide?');
16632                 if (!this.hideProgress && Roo.MessageBox) {
16633                     Roo.MessageBox.hide();
16634                 }
16635                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16636                 
16637                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16638                 
16639                 // THE END...
16640                 return false;   
16641             }
16642             
16643             var m = mods.shift();
16644             
16645             
16646             Roo.debug && Roo.log(m);
16647             // not sure if this is supported any more.. - modules that are are just function
16648             if (typeof(m) == 'function') { 
16649                 m.call(this);
16650                 return progressRun.defer(10, _this);
16651             } 
16652             
16653             
16654             msg = "Building Interface " + (total  - mods.length) + 
16655                     " of " + total + 
16656                     (m.name ? (' - ' + m.name) : '');
16657                         Roo.debug && Roo.log(msg);
16658             if (!_this.hideProgress &&  Roo.MessageBox) { 
16659                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16660             }
16661             
16662          
16663             // is the module disabled?
16664             var disabled = (typeof(m.disabled) == 'function') ?
16665                 m.disabled.call(m.module.disabled) : m.disabled;    
16666             
16667             
16668             if (disabled) {
16669                 return progressRun(); // we do not update the display!
16670             }
16671             
16672             // now build 
16673             
16674                         
16675                         
16676             m.render();
16677             // it's 10 on top level, and 1 on others??? why...
16678             return progressRun.defer(10, _this);
16679              
16680         }
16681         progressRun.defer(1, _this);
16682      
16683         
16684         
16685     },
16686     /**
16687      * Overlay a set of modified strings onto a component
16688      * This is dependant on our builder exporting the strings and 'named strings' elements.
16689      * 
16690      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16691      * @param {Object} associative array of 'named' string and it's new value.
16692      * 
16693      */
16694         overlayStrings : function( component, strings )
16695     {
16696         if (typeof(component['_named_strings']) == 'undefined') {
16697             throw "ERROR: component does not have _named_strings";
16698         }
16699         for ( var k in strings ) {
16700             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16701             if (md !== false) {
16702                 component['_strings'][md] = strings[k];
16703             } else {
16704                 Roo.log('could not find named string: ' + k + ' in');
16705                 Roo.log(component);
16706             }
16707             
16708         }
16709         
16710     },
16711     
16712         
16713         /**
16714          * Event Object.
16715          *
16716          *
16717          */
16718         event: false, 
16719     /**
16720          * wrapper for event.on - aliased later..  
16721          * Typically use to register a event handler for register:
16722          *
16723          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16724          *
16725          */
16726     on : false
16727    
16728     
16729     
16730 });
16731
16732 Roo.XComponent.event = new Roo.util.Observable({
16733                 events : { 
16734                         /**
16735                          * @event register
16736                          * Fires when an Component is registered,
16737                          * set the disable property on the Component to stop registration.
16738                          * @param {Roo.XComponent} c the component being registerd.
16739                          * 
16740                          */
16741                         'register' : true,
16742             /**
16743                          * @event beforebuild
16744                          * Fires before each Component is built
16745                          * can be used to apply permissions.
16746                          * @param {Roo.XComponent} c the component being registerd.
16747                          * 
16748                          */
16749                         'beforebuild' : true,
16750                         /**
16751                          * @event buildcomplete
16752                          * Fires on the top level element when all elements have been built
16753                          * @param {Roo.XComponent} the top level component.
16754                          */
16755                         'buildcomplete' : true
16756                         
16757                 }
16758 });
16759
16760 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16761  //
16762  /**
16763  * marked - a markdown parser
16764  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16765  * https://github.com/chjj/marked
16766  */
16767
16768
16769 /**
16770  *
16771  * Roo.Markdown - is a very crude wrapper around marked..
16772  *
16773  * usage:
16774  * 
16775  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16776  * 
16777  * Note: move the sample code to the bottom of this
16778  * file before uncommenting it.
16779  *
16780  */
16781
16782 Roo.Markdown = {};
16783 Roo.Markdown.toHtml = function(text) {
16784     
16785     var c = new Roo.Markdown.marked.setOptions({
16786             renderer: new Roo.Markdown.marked.Renderer(),
16787             gfm: true,
16788             tables: true,
16789             breaks: false,
16790             pedantic: false,
16791             sanitize: false,
16792             smartLists: true,
16793             smartypants: false
16794           });
16795     // A FEW HACKS!!?
16796     
16797     text = text.replace(/\\\n/g,' ');
16798     return Roo.Markdown.marked(text);
16799 };
16800 //
16801 // converter
16802 //
16803 // Wraps all "globals" so that the only thing
16804 // exposed is makeHtml().
16805 //
16806 (function() {
16807     
16808      /**
16809          * eval:var:escape
16810          * eval:var:unescape
16811          * eval:var:replace
16812          */
16813       
16814     /**
16815      * Helpers
16816      */
16817     
16818     var escape = function (html, encode) {
16819       return html
16820         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
16821         .replace(/</g, '&lt;')
16822         .replace(/>/g, '&gt;')
16823         .replace(/"/g, '&quot;')
16824         .replace(/'/g, '&#39;');
16825     }
16826     
16827     var unescape = function (html) {
16828         // explicitly match decimal, hex, and named HTML entities 
16829       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
16830         n = n.toLowerCase();
16831         if (n === 'colon') { return ':'; }
16832         if (n.charAt(0) === '#') {
16833           return n.charAt(1) === 'x'
16834             ? String.fromCharCode(parseInt(n.substring(2), 16))
16835             : String.fromCharCode(+n.substring(1));
16836         }
16837         return '';
16838       });
16839     }
16840     
16841     var replace = function (regex, opt) {
16842       regex = regex.source;
16843       opt = opt || '';
16844       return function self(name, val) {
16845         if (!name) { return new RegExp(regex, opt); }
16846         val = val.source || val;
16847         val = val.replace(/(^|[^\[])\^/g, '$1');
16848         regex = regex.replace(name, val);
16849         return self;
16850       };
16851     }
16852
16853
16854          /**
16855          * eval:var:noop
16856     */
16857     var noop = function () {}
16858     noop.exec = noop;
16859     
16860          /**
16861          * eval:var:merge
16862     */
16863     var merge = function (obj) {
16864       var i = 1
16865         , target
16866         , key;
16867     
16868       for (; i < arguments.length; i++) {
16869         target = arguments[i];
16870         for (key in target) {
16871           if (Object.prototype.hasOwnProperty.call(target, key)) {
16872             obj[key] = target[key];
16873           }
16874         }
16875       }
16876     
16877       return obj;
16878     }
16879     
16880     
16881     /**
16882      * Block-Level Grammar
16883      */
16884     
16885     
16886     
16887     
16888     var block = {
16889       newline: /^\n+/,
16890       code: /^( {4}[^\n]+\n*)+/,
16891       fences: noop,
16892       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16893       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16894       nptable: noop,
16895       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16896       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16897       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16898       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16899       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16900       table: noop,
16901       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16902       text: /^[^\n]+/
16903     };
16904     
16905     block.bullet = /(?:[*+-]|\d+\.)/;
16906     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16907     block.item = replace(block.item, 'gm')
16908       (/bull/g, block.bullet)
16909       ();
16910     
16911     block.list = replace(block.list)
16912       (/bull/g, block.bullet)
16913       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16914       ('def', '\\n+(?=' + block.def.source + ')')
16915       ();
16916     
16917     block.blockquote = replace(block.blockquote)
16918       ('def', block.def)
16919       ();
16920     
16921     block._tag = '(?!(?:'
16922       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16923       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16924       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16925     
16926     block.html = replace(block.html)
16927       ('comment', /<!--[\s\S]*?-->/)
16928       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16929       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16930       (/tag/g, block._tag)
16931       ();
16932     
16933     block.paragraph = replace(block.paragraph)
16934       ('hr', block.hr)
16935       ('heading', block.heading)
16936       ('lheading', block.lheading)
16937       ('blockquote', block.blockquote)
16938       ('tag', '<' + block._tag)
16939       ('def', block.def)
16940       ();
16941     
16942     /**
16943      * Normal Block Grammar
16944      */
16945     
16946     block.normal = merge({}, block);
16947     
16948     /**
16949      * GFM Block Grammar
16950      */
16951     
16952     block.gfm = merge({}, block.normal, {
16953       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16954       paragraph: /^/,
16955       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16956     });
16957     
16958     block.gfm.paragraph = replace(block.paragraph)
16959       ('(?!', '(?!'
16960         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16961         + block.list.source.replace('\\1', '\\3') + '|')
16962       ();
16963     
16964     /**
16965      * GFM + Tables Block Grammar
16966      */
16967     
16968     block.tables = merge({}, block.gfm, {
16969       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16970       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16971     });
16972     
16973     /**
16974      * Block Lexer
16975      */
16976     
16977     var Lexer = function (options) {
16978       this.tokens = [];
16979       this.tokens.links = {};
16980       this.options = options || marked.defaults;
16981       this.rules = block.normal;
16982     
16983       if (this.options.gfm) {
16984         if (this.options.tables) {
16985           this.rules = block.tables;
16986         } else {
16987           this.rules = block.gfm;
16988         }
16989       }
16990     }
16991     
16992     /**
16993      * Expose Block Rules
16994      */
16995     
16996     Lexer.rules = block;
16997     
16998     /**
16999      * Static Lex Method
17000      */
17001     
17002     Lexer.lex = function(src, options) {
17003       var lexer = new Lexer(options);
17004       return lexer.lex(src);
17005     };
17006     
17007     /**
17008      * Preprocessing
17009      */
17010     
17011     Lexer.prototype.lex = function(src) {
17012       src = src
17013         .replace(/\r\n|\r/g, '\n')
17014         .replace(/\t/g, '    ')
17015         .replace(/\u00a0/g, ' ')
17016         .replace(/\u2424/g, '\n');
17017     
17018       return this.token(src, true);
17019     };
17020     
17021     /**
17022      * Lexing
17023      */
17024     
17025     Lexer.prototype.token = function(src, top, bq) {
17026       var src = src.replace(/^ +$/gm, '')
17027         , next
17028         , loose
17029         , cap
17030         , bull
17031         , b
17032         , item
17033         , space
17034         , i
17035         , l;
17036     
17037       while (src) {
17038         // newline
17039         if (cap = this.rules.newline.exec(src)) {
17040           src = src.substring(cap[0].length);
17041           if (cap[0].length > 1) {
17042             this.tokens.push({
17043               type: 'space'
17044             });
17045           }
17046         }
17047     
17048         // code
17049         if (cap = this.rules.code.exec(src)) {
17050           src = src.substring(cap[0].length);
17051           cap = cap[0].replace(/^ {4}/gm, '');
17052           this.tokens.push({
17053             type: 'code',
17054             text: !this.options.pedantic
17055               ? cap.replace(/\n+$/, '')
17056               : cap
17057           });
17058           continue;
17059         }
17060     
17061         // fences (gfm)
17062         if (cap = this.rules.fences.exec(src)) {
17063           src = src.substring(cap[0].length);
17064           this.tokens.push({
17065             type: 'code',
17066             lang: cap[2],
17067             text: cap[3] || ''
17068           });
17069           continue;
17070         }
17071     
17072         // heading
17073         if (cap = this.rules.heading.exec(src)) {
17074           src = src.substring(cap[0].length);
17075           this.tokens.push({
17076             type: 'heading',
17077             depth: cap[1].length,
17078             text: cap[2]
17079           });
17080           continue;
17081         }
17082     
17083         // table no leading pipe (gfm)
17084         if (top && (cap = this.rules.nptable.exec(src))) {
17085           src = src.substring(cap[0].length);
17086     
17087           item = {
17088             type: 'table',
17089             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17090             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17091             cells: cap[3].replace(/\n$/, '').split('\n')
17092           };
17093     
17094           for (i = 0; i < item.align.length; i++) {
17095             if (/^ *-+: *$/.test(item.align[i])) {
17096               item.align[i] = 'right';
17097             } else if (/^ *:-+: *$/.test(item.align[i])) {
17098               item.align[i] = 'center';
17099             } else if (/^ *:-+ *$/.test(item.align[i])) {
17100               item.align[i] = 'left';
17101             } else {
17102               item.align[i] = null;
17103             }
17104           }
17105     
17106           for (i = 0; i < item.cells.length; i++) {
17107             item.cells[i] = item.cells[i].split(/ *\| */);
17108           }
17109     
17110           this.tokens.push(item);
17111     
17112           continue;
17113         }
17114     
17115         // lheading
17116         if (cap = this.rules.lheading.exec(src)) {
17117           src = src.substring(cap[0].length);
17118           this.tokens.push({
17119             type: 'heading',
17120             depth: cap[2] === '=' ? 1 : 2,
17121             text: cap[1]
17122           });
17123           continue;
17124         }
17125     
17126         // hr
17127         if (cap = this.rules.hr.exec(src)) {
17128           src = src.substring(cap[0].length);
17129           this.tokens.push({
17130             type: 'hr'
17131           });
17132           continue;
17133         }
17134     
17135         // blockquote
17136         if (cap = this.rules.blockquote.exec(src)) {
17137           src = src.substring(cap[0].length);
17138     
17139           this.tokens.push({
17140             type: 'blockquote_start'
17141           });
17142     
17143           cap = cap[0].replace(/^ *> ?/gm, '');
17144     
17145           // Pass `top` to keep the current
17146           // "toplevel" state. This is exactly
17147           // how markdown.pl works.
17148           this.token(cap, top, true);
17149     
17150           this.tokens.push({
17151             type: 'blockquote_end'
17152           });
17153     
17154           continue;
17155         }
17156     
17157         // list
17158         if (cap = this.rules.list.exec(src)) {
17159           src = src.substring(cap[0].length);
17160           bull = cap[2];
17161     
17162           this.tokens.push({
17163             type: 'list_start',
17164             ordered: bull.length > 1
17165           });
17166     
17167           // Get each top-level item.
17168           cap = cap[0].match(this.rules.item);
17169     
17170           next = false;
17171           l = cap.length;
17172           i = 0;
17173     
17174           for (; i < l; i++) {
17175             item = cap[i];
17176     
17177             // Remove the list item's bullet
17178             // so it is seen as the next token.
17179             space = item.length;
17180             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17181     
17182             // Outdent whatever the
17183             // list item contains. Hacky.
17184             if (~item.indexOf('\n ')) {
17185               space -= item.length;
17186               item = !this.options.pedantic
17187                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17188                 : item.replace(/^ {1,4}/gm, '');
17189             }
17190     
17191             // Determine whether the next list item belongs here.
17192             // Backpedal if it does not belong in this list.
17193             if (this.options.smartLists && i !== l - 1) {
17194               b = block.bullet.exec(cap[i + 1])[0];
17195               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17196                 src = cap.slice(i + 1).join('\n') + src;
17197                 i = l - 1;
17198               }
17199             }
17200     
17201             // Determine whether item is loose or not.
17202             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17203             // for discount behavior.
17204             loose = next || /\n\n(?!\s*$)/.test(item);
17205             if (i !== l - 1) {
17206               next = item.charAt(item.length - 1) === '\n';
17207               if (!loose) { loose = next; }
17208             }
17209     
17210             this.tokens.push({
17211               type: loose
17212                 ? 'loose_item_start'
17213                 : 'list_item_start'
17214             });
17215     
17216             // Recurse.
17217             this.token(item, false, bq);
17218     
17219             this.tokens.push({
17220               type: 'list_item_end'
17221             });
17222           }
17223     
17224           this.tokens.push({
17225             type: 'list_end'
17226           });
17227     
17228           continue;
17229         }
17230     
17231         // html
17232         if (cap = this.rules.html.exec(src)) {
17233           src = src.substring(cap[0].length);
17234           this.tokens.push({
17235             type: this.options.sanitize
17236               ? 'paragraph'
17237               : 'html',
17238             pre: !this.options.sanitizer
17239               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17240             text: cap[0]
17241           });
17242           continue;
17243         }
17244     
17245         // def
17246         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17247           src = src.substring(cap[0].length);
17248           this.tokens.links[cap[1].toLowerCase()] = {
17249             href: cap[2],
17250             title: cap[3]
17251           };
17252           continue;
17253         }
17254     
17255         // table (gfm)
17256         if (top && (cap = this.rules.table.exec(src))) {
17257           src = src.substring(cap[0].length);
17258     
17259           item = {
17260             type: 'table',
17261             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17262             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17263             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17264           };
17265     
17266           for (i = 0; i < item.align.length; i++) {
17267             if (/^ *-+: *$/.test(item.align[i])) {
17268               item.align[i] = 'right';
17269             } else if (/^ *:-+: *$/.test(item.align[i])) {
17270               item.align[i] = 'center';
17271             } else if (/^ *:-+ *$/.test(item.align[i])) {
17272               item.align[i] = 'left';
17273             } else {
17274               item.align[i] = null;
17275             }
17276           }
17277     
17278           for (i = 0; i < item.cells.length; i++) {
17279             item.cells[i] = item.cells[i]
17280               .replace(/^ *\| *| *\| *$/g, '')
17281               .split(/ *\| */);
17282           }
17283     
17284           this.tokens.push(item);
17285     
17286           continue;
17287         }
17288     
17289         // top-level paragraph
17290         if (top && (cap = this.rules.paragraph.exec(src))) {
17291           src = src.substring(cap[0].length);
17292           this.tokens.push({
17293             type: 'paragraph',
17294             text: cap[1].charAt(cap[1].length - 1) === '\n'
17295               ? cap[1].slice(0, -1)
17296               : cap[1]
17297           });
17298           continue;
17299         }
17300     
17301         // text
17302         if (cap = this.rules.text.exec(src)) {
17303           // Top-level should never reach here.
17304           src = src.substring(cap[0].length);
17305           this.tokens.push({
17306             type: 'text',
17307             text: cap[0]
17308           });
17309           continue;
17310         }
17311     
17312         if (src) {
17313           throw new
17314             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17315         }
17316       }
17317     
17318       return this.tokens;
17319     };
17320     
17321     /**
17322      * Inline-Level Grammar
17323      */
17324     
17325     var inline = {
17326       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17327       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17328       url: noop,
17329       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17330       link: /^!?\[(inside)\]\(href\)/,
17331       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17332       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17333       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17334       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17335       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17336       br: /^ {2,}\n(?!\s*$)/,
17337       del: noop,
17338       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17339     };
17340     
17341     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17342     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17343     
17344     inline.link = replace(inline.link)
17345       ('inside', inline._inside)
17346       ('href', inline._href)
17347       ();
17348     
17349     inline.reflink = replace(inline.reflink)
17350       ('inside', inline._inside)
17351       ();
17352     
17353     /**
17354      * Normal Inline Grammar
17355      */
17356     
17357     inline.normal = merge({}, inline);
17358     
17359     /**
17360      * Pedantic Inline Grammar
17361      */
17362     
17363     inline.pedantic = merge({}, inline.normal, {
17364       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17365       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17366     });
17367     
17368     /**
17369      * GFM Inline Grammar
17370      */
17371     
17372     inline.gfm = merge({}, inline.normal, {
17373       escape: replace(inline.escape)('])', '~|])')(),
17374       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17375       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17376       text: replace(inline.text)
17377         (']|', '~]|')
17378         ('|', '|https?://|')
17379         ()
17380     });
17381     
17382     /**
17383      * GFM + Line Breaks Inline Grammar
17384      */
17385     
17386     inline.breaks = merge({}, inline.gfm, {
17387       br: replace(inline.br)('{2,}', '*')(),
17388       text: replace(inline.gfm.text)('{2,}', '*')()
17389     });
17390     
17391     /**
17392      * Inline Lexer & Compiler
17393      */
17394     
17395     var InlineLexer  = function (links, options) {
17396       this.options = options || marked.defaults;
17397       this.links = links;
17398       this.rules = inline.normal;
17399       this.renderer = this.options.renderer || new Renderer;
17400       this.renderer.options = this.options;
17401     
17402       if (!this.links) {
17403         throw new
17404           Error('Tokens array requires a `links` property.');
17405       }
17406     
17407       if (this.options.gfm) {
17408         if (this.options.breaks) {
17409           this.rules = inline.breaks;
17410         } else {
17411           this.rules = inline.gfm;
17412         }
17413       } else if (this.options.pedantic) {
17414         this.rules = inline.pedantic;
17415       }
17416     }
17417     
17418     /**
17419      * Expose Inline Rules
17420      */
17421     
17422     InlineLexer.rules = inline;
17423     
17424     /**
17425      * Static Lexing/Compiling Method
17426      */
17427     
17428     InlineLexer.output = function(src, links, options) {
17429       var inline = new InlineLexer(links, options);
17430       return inline.output(src);
17431     };
17432     
17433     /**
17434      * Lexing/Compiling
17435      */
17436     
17437     InlineLexer.prototype.output = function(src) {
17438       var out = ''
17439         , link
17440         , text
17441         , href
17442         , cap;
17443     
17444       while (src) {
17445         // escape
17446         if (cap = this.rules.escape.exec(src)) {
17447           src = src.substring(cap[0].length);
17448           out += cap[1];
17449           continue;
17450         }
17451     
17452         // autolink
17453         if (cap = this.rules.autolink.exec(src)) {
17454           src = src.substring(cap[0].length);
17455           if (cap[2] === '@') {
17456             text = cap[1].charAt(6) === ':'
17457               ? this.mangle(cap[1].substring(7))
17458               : this.mangle(cap[1]);
17459             href = this.mangle('mailto:') + text;
17460           } else {
17461             text = escape(cap[1]);
17462             href = text;
17463           }
17464           out += this.renderer.link(href, null, text);
17465           continue;
17466         }
17467     
17468         // url (gfm)
17469         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17470           src = src.substring(cap[0].length);
17471           text = escape(cap[1]);
17472           href = text;
17473           out += this.renderer.link(href, null, text);
17474           continue;
17475         }
17476     
17477         // tag
17478         if (cap = this.rules.tag.exec(src)) {
17479           if (!this.inLink && /^<a /i.test(cap[0])) {
17480             this.inLink = true;
17481           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17482             this.inLink = false;
17483           }
17484           src = src.substring(cap[0].length);
17485           out += this.options.sanitize
17486             ? this.options.sanitizer
17487               ? this.options.sanitizer(cap[0])
17488               : escape(cap[0])
17489             : cap[0];
17490           continue;
17491         }
17492     
17493         // link
17494         if (cap = this.rules.link.exec(src)) {
17495           src = src.substring(cap[0].length);
17496           this.inLink = true;
17497           out += this.outputLink(cap, {
17498             href: cap[2],
17499             title: cap[3]
17500           });
17501           this.inLink = false;
17502           continue;
17503         }
17504     
17505         // reflink, nolink
17506         if ((cap = this.rules.reflink.exec(src))
17507             || (cap = this.rules.nolink.exec(src))) {
17508           src = src.substring(cap[0].length);
17509           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17510           link = this.links[link.toLowerCase()];
17511           if (!link || !link.href) {
17512             out += cap[0].charAt(0);
17513             src = cap[0].substring(1) + src;
17514             continue;
17515           }
17516           this.inLink = true;
17517           out += this.outputLink(cap, link);
17518           this.inLink = false;
17519           continue;
17520         }
17521     
17522         // strong
17523         if (cap = this.rules.strong.exec(src)) {
17524           src = src.substring(cap[0].length);
17525           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17526           continue;
17527         }
17528     
17529         // em
17530         if (cap = this.rules.em.exec(src)) {
17531           src = src.substring(cap[0].length);
17532           out += this.renderer.em(this.output(cap[2] || cap[1]));
17533           continue;
17534         }
17535     
17536         // code
17537         if (cap = this.rules.code.exec(src)) {
17538           src = src.substring(cap[0].length);
17539           out += this.renderer.codespan(escape(cap[2], true));
17540           continue;
17541         }
17542     
17543         // br
17544         if (cap = this.rules.br.exec(src)) {
17545           src = src.substring(cap[0].length);
17546           out += this.renderer.br();
17547           continue;
17548         }
17549     
17550         // del (gfm)
17551         if (cap = this.rules.del.exec(src)) {
17552           src = src.substring(cap[0].length);
17553           out += this.renderer.del(this.output(cap[1]));
17554           continue;
17555         }
17556     
17557         // text
17558         if (cap = this.rules.text.exec(src)) {
17559           src = src.substring(cap[0].length);
17560           out += this.renderer.text(escape(this.smartypants(cap[0])));
17561           continue;
17562         }
17563     
17564         if (src) {
17565           throw new
17566             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17567         }
17568       }
17569     
17570       return out;
17571     };
17572     
17573     /**
17574      * Compile Link
17575      */
17576     
17577     InlineLexer.prototype.outputLink = function(cap, link) {
17578       var href = escape(link.href)
17579         , title = link.title ? escape(link.title) : null;
17580     
17581       return cap[0].charAt(0) !== '!'
17582         ? this.renderer.link(href, title, this.output(cap[1]))
17583         : this.renderer.image(href, title, escape(cap[1]));
17584     };
17585     
17586     /**
17587      * Smartypants Transformations
17588      */
17589     
17590     InlineLexer.prototype.smartypants = function(text) {
17591       if (!this.options.smartypants)  { return text; }
17592       return text
17593         // em-dashes
17594         .replace(/---/g, '\u2014')
17595         // en-dashes
17596         .replace(/--/g, '\u2013')
17597         // opening singles
17598         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17599         // closing singles & apostrophes
17600         .replace(/'/g, '\u2019')
17601         // opening doubles
17602         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17603         // closing doubles
17604         .replace(/"/g, '\u201d')
17605         // ellipses
17606         .replace(/\.{3}/g, '\u2026');
17607     };
17608     
17609     /**
17610      * Mangle Links
17611      */
17612     
17613     InlineLexer.prototype.mangle = function(text) {
17614       if (!this.options.mangle) { return text; }
17615       var out = ''
17616         , l = text.length
17617         , i = 0
17618         , ch;
17619     
17620       for (; i < l; i++) {
17621         ch = text.charCodeAt(i);
17622         if (Math.random() > 0.5) {
17623           ch = 'x' + ch.toString(16);
17624         }
17625         out += '&#' + ch + ';';
17626       }
17627     
17628       return out;
17629     };
17630     
17631     /**
17632      * Renderer
17633      */
17634     
17635      /**
17636          * eval:var:Renderer
17637     */
17638     
17639     var Renderer   = function (options) {
17640       this.options = options || {};
17641     }
17642     
17643     Renderer.prototype.code = function(code, lang, escaped) {
17644       if (this.options.highlight) {
17645         var out = this.options.highlight(code, lang);
17646         if (out != null && out !== code) {
17647           escaped = true;
17648           code = out;
17649         }
17650       } else {
17651             // hack!!! - it's already escapeD?
17652             escaped = true;
17653       }
17654     
17655       if (!lang) {
17656         return '<pre><code>'
17657           + (escaped ? code : escape(code, true))
17658           + '\n</code></pre>';
17659       }
17660     
17661       return '<pre><code class="'
17662         + this.options.langPrefix
17663         + escape(lang, true)
17664         + '">'
17665         + (escaped ? code : escape(code, true))
17666         + '\n</code></pre>\n';
17667     };
17668     
17669     Renderer.prototype.blockquote = function(quote) {
17670       return '<blockquote>\n' + quote + '</blockquote>\n';
17671     };
17672     
17673     Renderer.prototype.html = function(html) {
17674       return html;
17675     };
17676     
17677     Renderer.prototype.heading = function(text, level, raw) {
17678       return '<h'
17679         + level
17680         + ' id="'
17681         + this.options.headerPrefix
17682         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17683         + '">'
17684         + text
17685         + '</h'
17686         + level
17687         + '>\n';
17688     };
17689     
17690     Renderer.prototype.hr = function() {
17691       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17692     };
17693     
17694     Renderer.prototype.list = function(body, ordered) {
17695       var type = ordered ? 'ol' : 'ul';
17696       return '<' + type + '>\n' + body + '</' + type + '>\n';
17697     };
17698     
17699     Renderer.prototype.listitem = function(text) {
17700       return '<li>' + text + '</li>\n';
17701     };
17702     
17703     Renderer.prototype.paragraph = function(text) {
17704       return '<p>' + text + '</p>\n';
17705     };
17706     
17707     Renderer.prototype.table = function(header, body) {
17708       return '<table class="table table-striped">\n'
17709         + '<thead>\n'
17710         + header
17711         + '</thead>\n'
17712         + '<tbody>\n'
17713         + body
17714         + '</tbody>\n'
17715         + '</table>\n';
17716     };
17717     
17718     Renderer.prototype.tablerow = function(content) {
17719       return '<tr>\n' + content + '</tr>\n';
17720     };
17721     
17722     Renderer.prototype.tablecell = function(content, flags) {
17723       var type = flags.header ? 'th' : 'td';
17724       var tag = flags.align
17725         ? '<' + type + ' style="text-align:' + flags.align + '">'
17726         : '<' + type + '>';
17727       return tag + content + '</' + type + '>\n';
17728     };
17729     
17730     // span level renderer
17731     Renderer.prototype.strong = function(text) {
17732       return '<strong>' + text + '</strong>';
17733     };
17734     
17735     Renderer.prototype.em = function(text) {
17736       return '<em>' + text + '</em>';
17737     };
17738     
17739     Renderer.prototype.codespan = function(text) {
17740       return '<code>' + text + '</code>';
17741     };
17742     
17743     Renderer.prototype.br = function() {
17744       return this.options.xhtml ? '<br/>' : '<br>';
17745     };
17746     
17747     Renderer.prototype.del = function(text) {
17748       return '<del>' + text + '</del>';
17749     };
17750     
17751     Renderer.prototype.link = function(href, title, text) {
17752       if (this.options.sanitize) {
17753         try {
17754           var prot = decodeURIComponent(unescape(href))
17755             .replace(/[^\w:]/g, '')
17756             .toLowerCase();
17757         } catch (e) {
17758           return '';
17759         }
17760         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17761           return '';
17762         }
17763       }
17764       var out = '<a href="' + href + '"';
17765       if (title) {
17766         out += ' title="' + title + '"';
17767       }
17768       out += '>' + text + '</a>';
17769       return out;
17770     };
17771     
17772     Renderer.prototype.image = function(href, title, text) {
17773       var out = '<img src="' + href + '" alt="' + text + '"';
17774       if (title) {
17775         out += ' title="' + title + '"';
17776       }
17777       out += this.options.xhtml ? '/>' : '>';
17778       return out;
17779     };
17780     
17781     Renderer.prototype.text = function(text) {
17782       return text;
17783     };
17784     
17785     /**
17786      * Parsing & Compiling
17787      */
17788          /**
17789          * eval:var:Parser
17790     */
17791     
17792     var Parser= function (options) {
17793       this.tokens = [];
17794       this.token = null;
17795       this.options = options || marked.defaults;
17796       this.options.renderer = this.options.renderer || new Renderer;
17797       this.renderer = this.options.renderer;
17798       this.renderer.options = this.options;
17799     }
17800     
17801     /**
17802      * Static Parse Method
17803      */
17804     
17805     Parser.parse = function(src, options, renderer) {
17806       var parser = new Parser(options, renderer);
17807       return parser.parse(src);
17808     };
17809     
17810     /**
17811      * Parse Loop
17812      */
17813     
17814     Parser.prototype.parse = function(src) {
17815       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17816       this.tokens = src.reverse();
17817     
17818       var out = '';
17819       while (this.next()) {
17820         out += this.tok();
17821       }
17822     
17823       return out;
17824     };
17825     
17826     /**
17827      * Next Token
17828      */
17829     
17830     Parser.prototype.next = function() {
17831       return this.token = this.tokens.pop();
17832     };
17833     
17834     /**
17835      * Preview Next Token
17836      */
17837     
17838     Parser.prototype.peek = function() {
17839       return this.tokens[this.tokens.length - 1] || 0;
17840     };
17841     
17842     /**
17843      * Parse Text Tokens
17844      */
17845     
17846     Parser.prototype.parseText = function() {
17847       var body = this.token.text;
17848     
17849       while (this.peek().type === 'text') {
17850         body += '\n' + this.next().text;
17851       }
17852     
17853       return this.inline.output(body);
17854     };
17855     
17856     /**
17857      * Parse Current Token
17858      */
17859     
17860     Parser.prototype.tok = function() {
17861       switch (this.token.type) {
17862         case 'space': {
17863           return '';
17864         }
17865         case 'hr': {
17866           return this.renderer.hr();
17867         }
17868         case 'heading': {
17869           return this.renderer.heading(
17870             this.inline.output(this.token.text),
17871             this.token.depth,
17872             this.token.text);
17873         }
17874         case 'code': {
17875           return this.renderer.code(this.token.text,
17876             this.token.lang,
17877             this.token.escaped);
17878         }
17879         case 'table': {
17880           var header = ''
17881             , body = ''
17882             , i
17883             , row
17884             , cell
17885             , flags
17886             , j;
17887     
17888           // header
17889           cell = '';
17890           for (i = 0; i < this.token.header.length; i++) {
17891             flags = { header: true, align: this.token.align[i] };
17892             cell += this.renderer.tablecell(
17893               this.inline.output(this.token.header[i]),
17894               { header: true, align: this.token.align[i] }
17895             );
17896           }
17897           header += this.renderer.tablerow(cell);
17898     
17899           for (i = 0; i < this.token.cells.length; i++) {
17900             row = this.token.cells[i];
17901     
17902             cell = '';
17903             for (j = 0; j < row.length; j++) {
17904               cell += this.renderer.tablecell(
17905                 this.inline.output(row[j]),
17906                 { header: false, align: this.token.align[j] }
17907               );
17908             }
17909     
17910             body += this.renderer.tablerow(cell);
17911           }
17912           return this.renderer.table(header, body);
17913         }
17914         case 'blockquote_start': {
17915           var body = '';
17916     
17917           while (this.next().type !== 'blockquote_end') {
17918             body += this.tok();
17919           }
17920     
17921           return this.renderer.blockquote(body);
17922         }
17923         case 'list_start': {
17924           var body = ''
17925             , ordered = this.token.ordered;
17926     
17927           while (this.next().type !== 'list_end') {
17928             body += this.tok();
17929           }
17930     
17931           return this.renderer.list(body, ordered);
17932         }
17933         case 'list_item_start': {
17934           var body = '';
17935     
17936           while (this.next().type !== 'list_item_end') {
17937             body += this.token.type === 'text'
17938               ? this.parseText()
17939               : this.tok();
17940           }
17941     
17942           return this.renderer.listitem(body);
17943         }
17944         case 'loose_item_start': {
17945           var body = '';
17946     
17947           while (this.next().type !== 'list_item_end') {
17948             body += this.tok();
17949           }
17950     
17951           return this.renderer.listitem(body);
17952         }
17953         case 'html': {
17954           var html = !this.token.pre && !this.options.pedantic
17955             ? this.inline.output(this.token.text)
17956             : this.token.text;
17957           return this.renderer.html(html);
17958         }
17959         case 'paragraph': {
17960           return this.renderer.paragraph(this.inline.output(this.token.text));
17961         }
17962         case 'text': {
17963           return this.renderer.paragraph(this.parseText());
17964         }
17965       }
17966     };
17967   
17968     
17969     /**
17970      * Marked
17971      */
17972          /**
17973          * eval:var:marked
17974     */
17975     var marked = function (src, opt, callback) {
17976       if (callback || typeof opt === 'function') {
17977         if (!callback) {
17978           callback = opt;
17979           opt = null;
17980         }
17981     
17982         opt = merge({}, marked.defaults, opt || {});
17983     
17984         var highlight = opt.highlight
17985           , tokens
17986           , pending
17987           , i = 0;
17988     
17989         try {
17990           tokens = Lexer.lex(src, opt)
17991         } catch (e) {
17992           return callback(e);
17993         }
17994     
17995         pending = tokens.length;
17996          /**
17997          * eval:var:done
17998     */
17999         var done = function(err) {
18000           if (err) {
18001             opt.highlight = highlight;
18002             return callback(err);
18003           }
18004     
18005           var out;
18006     
18007           try {
18008             out = Parser.parse(tokens, opt);
18009           } catch (e) {
18010             err = e;
18011           }
18012     
18013           opt.highlight = highlight;
18014     
18015           return err
18016             ? callback(err)
18017             : callback(null, out);
18018         };
18019     
18020         if (!highlight || highlight.length < 3) {
18021           return done();
18022         }
18023     
18024         delete opt.highlight;
18025     
18026         if (!pending) { return done(); }
18027     
18028         for (; i < tokens.length; i++) {
18029           (function(token) {
18030             if (token.type !== 'code') {
18031               return --pending || done();
18032             }
18033             return highlight(token.text, token.lang, function(err, code) {
18034               if (err) { return done(err); }
18035               if (code == null || code === token.text) {
18036                 return --pending || done();
18037               }
18038               token.text = code;
18039               token.escaped = true;
18040               --pending || done();
18041             });
18042           })(tokens[i]);
18043         }
18044     
18045         return;
18046       }
18047       try {
18048         if (opt) { opt = merge({}, marked.defaults, opt); }
18049         return Parser.parse(Lexer.lex(src, opt), opt);
18050       } catch (e) {
18051         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18052         if ((opt || marked.defaults).silent) {
18053           return '<p>An error occured:</p><pre>'
18054             + escape(e.message + '', true)
18055             + '</pre>';
18056         }
18057         throw e;
18058       }
18059     }
18060     
18061     /**
18062      * Options
18063      */
18064     
18065     marked.options =
18066     marked.setOptions = function(opt) {
18067       merge(marked.defaults, opt);
18068       return marked;
18069     };
18070     
18071     marked.defaults = {
18072       gfm: true,
18073       tables: true,
18074       breaks: false,
18075       pedantic: false,
18076       sanitize: false,
18077       sanitizer: null,
18078       mangle: true,
18079       smartLists: false,
18080       silent: false,
18081       highlight: null,
18082       langPrefix: 'lang-',
18083       smartypants: false,
18084       headerPrefix: '',
18085       renderer: new Renderer,
18086       xhtml: false
18087     };
18088     
18089     /**
18090      * Expose
18091      */
18092     
18093     marked.Parser = Parser;
18094     marked.parser = Parser.parse;
18095     
18096     marked.Renderer = Renderer;
18097     
18098     marked.Lexer = Lexer;
18099     marked.lexer = Lexer.lex;
18100     
18101     marked.InlineLexer = InlineLexer;
18102     marked.inlineLexer = InlineLexer.output;
18103     
18104     marked.parse = marked;
18105     
18106     Roo.Markdown.marked = marked;
18107
18108 })();/*
18109  * Based on:
18110  * Ext JS Library 1.1.1
18111  * Copyright(c) 2006-2007, Ext JS, LLC.
18112  *
18113  * Originally Released Under LGPL - original licence link has changed is not relivant.
18114  *
18115  * Fork - LGPL
18116  * <script type="text/javascript">
18117  */
18118
18119
18120
18121 /*
18122  * These classes are derivatives of the similarly named classes in the YUI Library.
18123  * The original license:
18124  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18125  * Code licensed under the BSD License:
18126  * http://developer.yahoo.net/yui/license.txt
18127  */
18128
18129 (function() {
18130
18131 var Event=Roo.EventManager;
18132 var Dom=Roo.lib.Dom;
18133
18134 /**
18135  * @class Roo.dd.DragDrop
18136  * @extends Roo.util.Observable
18137  * Defines the interface and base operation of items that that can be
18138  * dragged or can be drop targets.  It was designed to be extended, overriding
18139  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18140  * Up to three html elements can be associated with a DragDrop instance:
18141  * <ul>
18142  * <li>linked element: the element that is passed into the constructor.
18143  * This is the element which defines the boundaries for interaction with
18144  * other DragDrop objects.</li>
18145  * <li>handle element(s): The drag operation only occurs if the element that
18146  * was clicked matches a handle element.  By default this is the linked
18147  * element, but there are times that you will want only a portion of the
18148  * linked element to initiate the drag operation, and the setHandleElId()
18149  * method provides a way to define this.</li>
18150  * <li>drag element: this represents the element that would be moved along
18151  * with the cursor during a drag operation.  By default, this is the linked
18152  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18153  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18154  * </li>
18155  * </ul>
18156  * This class should not be instantiated until the onload event to ensure that
18157  * the associated elements are available.
18158  * The following would define a DragDrop obj that would interact with any
18159  * other DragDrop obj in the "group1" group:
18160  * <pre>
18161  *  dd = new Roo.dd.DragDrop("div1", "group1");
18162  * </pre>
18163  * Since none of the event handlers have been implemented, nothing would
18164  * actually happen if you were to run the code above.  Normally you would
18165  * override this class or one of the default implementations, but you can
18166  * also override the methods you want on an instance of the class...
18167  * <pre>
18168  *  dd.onDragDrop = function(e, id) {
18169  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18170  *  }
18171  * </pre>
18172  * @constructor
18173  * @param {String} id of the element that is linked to this instance
18174  * @param {String} sGroup the group of related DragDrop objects
18175  * @param {object} config an object containing configurable attributes
18176  *                Valid properties for DragDrop:
18177  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18178  */
18179 Roo.dd.DragDrop = function(id, sGroup, config) {
18180     if (id) {
18181         this.init(id, sGroup, config);
18182     }
18183     
18184 };
18185
18186 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18187
18188     /**
18189      * The id of the element associated with this object.  This is what we
18190      * refer to as the "linked element" because the size and position of
18191      * this element is used to determine when the drag and drop objects have
18192      * interacted.
18193      * @property id
18194      * @type String
18195      */
18196     id: null,
18197
18198     /**
18199      * Configuration attributes passed into the constructor
18200      * @property config
18201      * @type object
18202      */
18203     config: null,
18204
18205     /**
18206      * The id of the element that will be dragged.  By default this is same
18207      * as the linked element , but could be changed to another element. Ex:
18208      * Roo.dd.DDProxy
18209      * @property dragElId
18210      * @type String
18211      * @private
18212      */
18213     dragElId: null,
18214
18215     /**
18216      * the id of the element that initiates the drag operation.  By default
18217      * this is the linked element, but could be changed to be a child of this
18218      * element.  This lets us do things like only starting the drag when the
18219      * header element within the linked html element is clicked.
18220      * @property handleElId
18221      * @type String
18222      * @private
18223      */
18224     handleElId: null,
18225
18226     /**
18227      * An associative array of HTML tags that will be ignored if clicked.
18228      * @property invalidHandleTypes
18229      * @type {string: string}
18230      */
18231     invalidHandleTypes: null,
18232
18233     /**
18234      * An associative array of ids for elements that will be ignored if clicked
18235      * @property invalidHandleIds
18236      * @type {string: string}
18237      */
18238     invalidHandleIds: null,
18239
18240     /**
18241      * An indexted array of css class names for elements that will be ignored
18242      * if clicked.
18243      * @property invalidHandleClasses
18244      * @type string[]
18245      */
18246     invalidHandleClasses: null,
18247
18248     /**
18249      * The linked element's absolute X position at the time the drag was
18250      * started
18251      * @property startPageX
18252      * @type int
18253      * @private
18254      */
18255     startPageX: 0,
18256
18257     /**
18258      * The linked element's absolute X position at the time the drag was
18259      * started
18260      * @property startPageY
18261      * @type int
18262      * @private
18263      */
18264     startPageY: 0,
18265
18266     /**
18267      * The group defines a logical collection of DragDrop objects that are
18268      * related.  Instances only get events when interacting with other
18269      * DragDrop object in the same group.  This lets us define multiple
18270      * groups using a single DragDrop subclass if we want.
18271      * @property groups
18272      * @type {string: string}
18273      */
18274     groups: null,
18275
18276     /**
18277      * Individual drag/drop instances can be locked.  This will prevent
18278      * onmousedown start drag.
18279      * @property locked
18280      * @type boolean
18281      * @private
18282      */
18283     locked: false,
18284
18285     /**
18286      * Lock this instance
18287      * @method lock
18288      */
18289     lock: function() { this.locked = true; },
18290
18291     /**
18292      * Unlock this instace
18293      * @method unlock
18294      */
18295     unlock: function() { this.locked = false; },
18296
18297     /**
18298      * By default, all insances can be a drop target.  This can be disabled by
18299      * setting isTarget to false.
18300      * @method isTarget
18301      * @type boolean
18302      */
18303     isTarget: true,
18304
18305     /**
18306      * The padding configured for this drag and drop object for calculating
18307      * the drop zone intersection with this object.
18308      * @method padding
18309      * @type int[]
18310      */
18311     padding: null,
18312
18313     /**
18314      * Cached reference to the linked element
18315      * @property _domRef
18316      * @private
18317      */
18318     _domRef: null,
18319
18320     /**
18321      * Internal typeof flag
18322      * @property __ygDragDrop
18323      * @private
18324      */
18325     __ygDragDrop: true,
18326
18327     /**
18328      * Set to true when horizontal contraints are applied
18329      * @property constrainX
18330      * @type boolean
18331      * @private
18332      */
18333     constrainX: false,
18334
18335     /**
18336      * Set to true when vertical contraints are applied
18337      * @property constrainY
18338      * @type boolean
18339      * @private
18340      */
18341     constrainY: false,
18342
18343     /**
18344      * The left constraint
18345      * @property minX
18346      * @type int
18347      * @private
18348      */
18349     minX: 0,
18350
18351     /**
18352      * The right constraint
18353      * @property maxX
18354      * @type int
18355      * @private
18356      */
18357     maxX: 0,
18358
18359     /**
18360      * The up constraint
18361      * @property minY
18362      * @type int
18363      * @type int
18364      * @private
18365      */
18366     minY: 0,
18367
18368     /**
18369      * The down constraint
18370      * @property maxY
18371      * @type int
18372      * @private
18373      */
18374     maxY: 0,
18375
18376     /**
18377      * Maintain offsets when we resetconstraints.  Set to true when you want
18378      * the position of the element relative to its parent to stay the same
18379      * when the page changes
18380      *
18381      * @property maintainOffset
18382      * @type boolean
18383      */
18384     maintainOffset: false,
18385
18386     /**
18387      * Array of pixel locations the element will snap to if we specified a
18388      * horizontal graduation/interval.  This array is generated automatically
18389      * when you define a tick interval.
18390      * @property xTicks
18391      * @type int[]
18392      */
18393     xTicks: null,
18394
18395     /**
18396      * Array of pixel locations the element will snap to if we specified a
18397      * vertical graduation/interval.  This array is generated automatically
18398      * when you define a tick interval.
18399      * @property yTicks
18400      * @type int[]
18401      */
18402     yTicks: null,
18403
18404     /**
18405      * By default the drag and drop instance will only respond to the primary
18406      * button click (left button for a right-handed mouse).  Set to true to
18407      * allow drag and drop to start with any mouse click that is propogated
18408      * by the browser
18409      * @property primaryButtonOnly
18410      * @type boolean
18411      */
18412     primaryButtonOnly: true,
18413
18414     /**
18415      * The availabe property is false until the linked dom element is accessible.
18416      * @property available
18417      * @type boolean
18418      */
18419     available: false,
18420
18421     /**
18422      * By default, drags can only be initiated if the mousedown occurs in the
18423      * region the linked element is.  This is done in part to work around a
18424      * bug in some browsers that mis-report the mousedown if the previous
18425      * mouseup happened outside of the window.  This property is set to true
18426      * if outer handles are defined.
18427      *
18428      * @property hasOuterHandles
18429      * @type boolean
18430      * @default false
18431      */
18432     hasOuterHandles: false,
18433
18434     /**
18435      * Code that executes immediately before the startDrag event
18436      * @method b4StartDrag
18437      * @private
18438      */
18439     b4StartDrag: function(x, y) { },
18440
18441     /**
18442      * Abstract method called after a drag/drop object is clicked
18443      * and the drag or mousedown time thresholds have beeen met.
18444      * @method startDrag
18445      * @param {int} X click location
18446      * @param {int} Y click location
18447      */
18448     startDrag: function(x, y) { /* override this */ },
18449
18450     /**
18451      * Code that executes immediately before the onDrag event
18452      * @method b4Drag
18453      * @private
18454      */
18455     b4Drag: function(e) { },
18456
18457     /**
18458      * Abstract method called during the onMouseMove event while dragging an
18459      * object.
18460      * @method onDrag
18461      * @param {Event} e the mousemove event
18462      */
18463     onDrag: function(e) { /* override this */ },
18464
18465     /**
18466      * Abstract method called when this element fist begins hovering over
18467      * another DragDrop obj
18468      * @method onDragEnter
18469      * @param {Event} e the mousemove event
18470      * @param {String|DragDrop[]} id In POINT mode, the element
18471      * id this is hovering over.  In INTERSECT mode, an array of one or more
18472      * dragdrop items being hovered over.
18473      */
18474     onDragEnter: function(e, id) { /* override this */ },
18475
18476     /**
18477      * Code that executes immediately before the onDragOver event
18478      * @method b4DragOver
18479      * @private
18480      */
18481     b4DragOver: function(e) { },
18482
18483     /**
18484      * Abstract method called when this element is hovering over another
18485      * DragDrop obj
18486      * @method onDragOver
18487      * @param {Event} e the mousemove event
18488      * @param {String|DragDrop[]} id In POINT mode, the element
18489      * id this is hovering over.  In INTERSECT mode, an array of dd items
18490      * being hovered over.
18491      */
18492     onDragOver: function(e, id) { /* override this */ },
18493
18494     /**
18495      * Code that executes immediately before the onDragOut event
18496      * @method b4DragOut
18497      * @private
18498      */
18499     b4DragOut: function(e) { },
18500
18501     /**
18502      * Abstract method called when we are no longer hovering over an element
18503      * @method onDragOut
18504      * @param {Event} e the mousemove event
18505      * @param {String|DragDrop[]} id In POINT mode, the element
18506      * id this was hovering over.  In INTERSECT mode, an array of dd items
18507      * that the mouse is no longer over.
18508      */
18509     onDragOut: function(e, id) { /* override this */ },
18510
18511     /**
18512      * Code that executes immediately before the onDragDrop event
18513      * @method b4DragDrop
18514      * @private
18515      */
18516     b4DragDrop: function(e) { },
18517
18518     /**
18519      * Abstract method called when this item is dropped on another DragDrop
18520      * obj
18521      * @method onDragDrop
18522      * @param {Event} e the mouseup event
18523      * @param {String|DragDrop[]} id In POINT mode, the element
18524      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18525      * was dropped on.
18526      */
18527     onDragDrop: function(e, id) { /* override this */ },
18528
18529     /**
18530      * Abstract method called when this item is dropped on an area with no
18531      * drop target
18532      * @method onInvalidDrop
18533      * @param {Event} e the mouseup event
18534      */
18535     onInvalidDrop: function(e) { /* override this */ },
18536
18537     /**
18538      * Code that executes immediately before the endDrag event
18539      * @method b4EndDrag
18540      * @private
18541      */
18542     b4EndDrag: function(e) { },
18543
18544     /**
18545      * Fired when we are done dragging the object
18546      * @method endDrag
18547      * @param {Event} e the mouseup event
18548      */
18549     endDrag: function(e) { /* override this */ },
18550
18551     /**
18552      * Code executed immediately before the onMouseDown event
18553      * @method b4MouseDown
18554      * @param {Event} e the mousedown event
18555      * @private
18556      */
18557     b4MouseDown: function(e) {  },
18558
18559     /**
18560      * Event handler that fires when a drag/drop obj gets a mousedown
18561      * @method onMouseDown
18562      * @param {Event} e the mousedown event
18563      */
18564     onMouseDown: function(e) { /* override this */ },
18565
18566     /**
18567      * Event handler that fires when a drag/drop obj gets a mouseup
18568      * @method onMouseUp
18569      * @param {Event} e the mouseup event
18570      */
18571     onMouseUp: function(e) { /* override this */ },
18572
18573     /**
18574      * Override the onAvailable method to do what is needed after the initial
18575      * position was determined.
18576      * @method onAvailable
18577      */
18578     onAvailable: function () {
18579     },
18580
18581     /*
18582      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18583      * @type Object
18584      */
18585     defaultPadding : {left:0, right:0, top:0, bottom:0},
18586
18587     /*
18588      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18589  *
18590  * Usage:
18591  <pre><code>
18592  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18593                 { dragElId: "existingProxyDiv" });
18594  dd.startDrag = function(){
18595      this.constrainTo("parent-id");
18596  };
18597  </code></pre>
18598  * Or you can initalize it using the {@link Roo.Element} object:
18599  <pre><code>
18600  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18601      startDrag : function(){
18602          this.constrainTo("parent-id");
18603      }
18604  });
18605  </code></pre>
18606      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18607      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18608      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18609      * an object containing the sides to pad. For example: {right:10, bottom:10}
18610      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18611      */
18612     constrainTo : function(constrainTo, pad, inContent){
18613         if(typeof pad == "number"){
18614             pad = {left: pad, right:pad, top:pad, bottom:pad};
18615         }
18616         pad = pad || this.defaultPadding;
18617         var b = Roo.get(this.getEl()).getBox();
18618         var ce = Roo.get(constrainTo);
18619         var s = ce.getScroll();
18620         var c, cd = ce.dom;
18621         if(cd == document.body){
18622             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18623         }else{
18624             xy = ce.getXY();
18625             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18626         }
18627
18628
18629         var topSpace = b.y - c.y;
18630         var leftSpace = b.x - c.x;
18631
18632         this.resetConstraints();
18633         this.setXConstraint(leftSpace - (pad.left||0), // left
18634                 c.width - leftSpace - b.width - (pad.right||0) //right
18635         );
18636         this.setYConstraint(topSpace - (pad.top||0), //top
18637                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18638         );
18639     },
18640
18641     /**
18642      * Returns a reference to the linked element
18643      * @method getEl
18644      * @return {HTMLElement} the html element
18645      */
18646     getEl: function() {
18647         if (!this._domRef) {
18648             this._domRef = Roo.getDom(this.id);
18649         }
18650
18651         return this._domRef;
18652     },
18653
18654     /**
18655      * Returns a reference to the actual element to drag.  By default this is
18656      * the same as the html element, but it can be assigned to another
18657      * element. An example of this can be found in Roo.dd.DDProxy
18658      * @method getDragEl
18659      * @return {HTMLElement} the html element
18660      */
18661     getDragEl: function() {
18662         return Roo.getDom(this.dragElId);
18663     },
18664
18665     /**
18666      * Sets up the DragDrop object.  Must be called in the constructor of any
18667      * Roo.dd.DragDrop subclass
18668      * @method init
18669      * @param id the id of the linked element
18670      * @param {String} sGroup the group of related items
18671      * @param {object} config configuration attributes
18672      */
18673     init: function(id, sGroup, config) {
18674         this.initTarget(id, sGroup, config);
18675         if (!Roo.isTouch) {
18676             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18677         }
18678         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18679         // Event.on(this.id, "selectstart", Event.preventDefault);
18680     },
18681
18682     /**
18683      * Initializes Targeting functionality only... the object does not
18684      * get a mousedown handler.
18685      * @method initTarget
18686      * @param id the id of the linked element
18687      * @param {String} sGroup the group of related items
18688      * @param {object} config configuration attributes
18689      */
18690     initTarget: function(id, sGroup, config) {
18691
18692         // configuration attributes
18693         this.config = config || {};
18694
18695         // create a local reference to the drag and drop manager
18696         this.DDM = Roo.dd.DDM;
18697         // initialize the groups array
18698         this.groups = {};
18699
18700         // assume that we have an element reference instead of an id if the
18701         // parameter is not a string
18702         if (typeof id !== "string") {
18703             id = Roo.id(id);
18704         }
18705
18706         // set the id
18707         this.id = id;
18708
18709         // add to an interaction group
18710         this.addToGroup((sGroup) ? sGroup : "default");
18711
18712         // We don't want to register this as the handle with the manager
18713         // so we just set the id rather than calling the setter.
18714         this.handleElId = id;
18715
18716         // the linked element is the element that gets dragged by default
18717         this.setDragElId(id);
18718
18719         // by default, clicked anchors will not start drag operations.
18720         this.invalidHandleTypes = { A: "A" };
18721         this.invalidHandleIds = {};
18722         this.invalidHandleClasses = [];
18723
18724         this.applyConfig();
18725
18726         this.handleOnAvailable();
18727     },
18728
18729     /**
18730      * Applies the configuration parameters that were passed into the constructor.
18731      * This is supposed to happen at each level through the inheritance chain.  So
18732      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18733      * DragDrop in order to get all of the parameters that are available in
18734      * each object.
18735      * @method applyConfig
18736      */
18737     applyConfig: function() {
18738
18739         // configurable properties:
18740         //    padding, isTarget, maintainOffset, primaryButtonOnly
18741         this.padding           = this.config.padding || [0, 0, 0, 0];
18742         this.isTarget          = (this.config.isTarget !== false);
18743         this.maintainOffset    = (this.config.maintainOffset);
18744         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18745
18746     },
18747
18748     /**
18749      * Executed when the linked element is available
18750      * @method handleOnAvailable
18751      * @private
18752      */
18753     handleOnAvailable: function() {
18754         this.available = true;
18755         this.resetConstraints();
18756         this.onAvailable();
18757     },
18758
18759      /**
18760      * Configures the padding for the target zone in px.  Effectively expands
18761      * (or reduces) the virtual object size for targeting calculations.
18762      * Supports css-style shorthand; if only one parameter is passed, all sides
18763      * will have that padding, and if only two are passed, the top and bottom
18764      * will have the first param, the left and right the second.
18765      * @method setPadding
18766      * @param {int} iTop    Top pad
18767      * @param {int} iRight  Right pad
18768      * @param {int} iBot    Bot pad
18769      * @param {int} iLeft   Left pad
18770      */
18771     setPadding: function(iTop, iRight, iBot, iLeft) {
18772         // this.padding = [iLeft, iRight, iTop, iBot];
18773         if (!iRight && 0 !== iRight) {
18774             this.padding = [iTop, iTop, iTop, iTop];
18775         } else if (!iBot && 0 !== iBot) {
18776             this.padding = [iTop, iRight, iTop, iRight];
18777         } else {
18778             this.padding = [iTop, iRight, iBot, iLeft];
18779         }
18780     },
18781
18782     /**
18783      * Stores the initial placement of the linked element.
18784      * @method setInitialPosition
18785      * @param {int} diffX   the X offset, default 0
18786      * @param {int} diffY   the Y offset, default 0
18787      */
18788     setInitPosition: function(diffX, diffY) {
18789         var el = this.getEl();
18790
18791         if (!this.DDM.verifyEl(el)) {
18792             return;
18793         }
18794
18795         var dx = diffX || 0;
18796         var dy = diffY || 0;
18797
18798         var p = Dom.getXY( el );
18799
18800         this.initPageX = p[0] - dx;
18801         this.initPageY = p[1] - dy;
18802
18803         this.lastPageX = p[0];
18804         this.lastPageY = p[1];
18805
18806
18807         this.setStartPosition(p);
18808     },
18809
18810     /**
18811      * Sets the start position of the element.  This is set when the obj
18812      * is initialized, the reset when a drag is started.
18813      * @method setStartPosition
18814      * @param pos current position (from previous lookup)
18815      * @private
18816      */
18817     setStartPosition: function(pos) {
18818         var p = pos || Dom.getXY( this.getEl() );
18819         this.deltaSetXY = null;
18820
18821         this.startPageX = p[0];
18822         this.startPageY = p[1];
18823     },
18824
18825     /**
18826      * Add this instance to a group of related drag/drop objects.  All
18827      * instances belong to at least one group, and can belong to as many
18828      * groups as needed.
18829      * @method addToGroup
18830      * @param sGroup {string} the name of the group
18831      */
18832     addToGroup: function(sGroup) {
18833         this.groups[sGroup] = true;
18834         this.DDM.regDragDrop(this, sGroup);
18835     },
18836
18837     /**
18838      * Remove's this instance from the supplied interaction group
18839      * @method removeFromGroup
18840      * @param {string}  sGroup  The group to drop
18841      */
18842     removeFromGroup: function(sGroup) {
18843         if (this.groups[sGroup]) {
18844             delete this.groups[sGroup];
18845         }
18846
18847         this.DDM.removeDDFromGroup(this, sGroup);
18848     },
18849
18850     /**
18851      * Allows you to specify that an element other than the linked element
18852      * will be moved with the cursor during a drag
18853      * @method setDragElId
18854      * @param id {string} the id of the element that will be used to initiate the drag
18855      */
18856     setDragElId: function(id) {
18857         this.dragElId = id;
18858     },
18859
18860     /**
18861      * Allows you to specify a child of the linked element that should be
18862      * used to initiate the drag operation.  An example of this would be if
18863      * you have a content div with text and links.  Clicking anywhere in the
18864      * content area would normally start the drag operation.  Use this method
18865      * to specify that an element inside of the content div is the element
18866      * that starts the drag operation.
18867      * @method setHandleElId
18868      * @param id {string} the id of the element that will be used to
18869      * initiate the drag.
18870      */
18871     setHandleElId: function(id) {
18872         if (typeof id !== "string") {
18873             id = Roo.id(id);
18874         }
18875         this.handleElId = id;
18876         this.DDM.regHandle(this.id, id);
18877     },
18878
18879     /**
18880      * Allows you to set an element outside of the linked element as a drag
18881      * handle
18882      * @method setOuterHandleElId
18883      * @param id the id of the element that will be used to initiate the drag
18884      */
18885     setOuterHandleElId: function(id) {
18886         if (typeof id !== "string") {
18887             id = Roo.id(id);
18888         }
18889         Event.on(id, "mousedown",
18890                 this.handleMouseDown, this);
18891         this.setHandleElId(id);
18892
18893         this.hasOuterHandles = true;
18894     },
18895
18896     /**
18897      * Remove all drag and drop hooks for this element
18898      * @method unreg
18899      */
18900     unreg: function() {
18901         Event.un(this.id, "mousedown",
18902                 this.handleMouseDown);
18903         Event.un(this.id, "touchstart",
18904                 this.handleMouseDown);
18905         this._domRef = null;
18906         this.DDM._remove(this);
18907     },
18908
18909     destroy : function(){
18910         this.unreg();
18911     },
18912
18913     /**
18914      * Returns true if this instance is locked, or the drag drop mgr is locked
18915      * (meaning that all drag/drop is disabled on the page.)
18916      * @method isLocked
18917      * @return {boolean} true if this obj or all drag/drop is locked, else
18918      * false
18919      */
18920     isLocked: function() {
18921         return (this.DDM.isLocked() || this.locked);
18922     },
18923
18924     /**
18925      * Fired when this object is clicked
18926      * @method handleMouseDown
18927      * @param {Event} e
18928      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18929      * @private
18930      */
18931     handleMouseDown: function(e, oDD){
18932      
18933         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18934             //Roo.log('not touch/ button !=0');
18935             return;
18936         }
18937         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18938             return; // double touch..
18939         }
18940         
18941
18942         if (this.isLocked()) {
18943             //Roo.log('locked');
18944             return;
18945         }
18946
18947         this.DDM.refreshCache(this.groups);
18948 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18949         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18950         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18951             //Roo.log('no outer handes or not over target');
18952                 // do nothing.
18953         } else {
18954 //            Roo.log('check validator');
18955             if (this.clickValidator(e)) {
18956 //                Roo.log('validate success');
18957                 // set the initial element position
18958                 this.setStartPosition();
18959
18960
18961                 this.b4MouseDown(e);
18962                 this.onMouseDown(e);
18963
18964                 this.DDM.handleMouseDown(e, this);
18965
18966                 this.DDM.stopEvent(e);
18967             } else {
18968
18969
18970             }
18971         }
18972     },
18973
18974     clickValidator: function(e) {
18975         var target = e.getTarget();
18976         return ( this.isValidHandleChild(target) &&
18977                     (this.id == this.handleElId ||
18978                         this.DDM.handleWasClicked(target, this.id)) );
18979     },
18980
18981     /**
18982      * Allows you to specify a tag name that should not start a drag operation
18983      * when clicked.  This is designed to facilitate embedding links within a
18984      * drag handle that do something other than start the drag.
18985      * @method addInvalidHandleType
18986      * @param {string} tagName the type of element to exclude
18987      */
18988     addInvalidHandleType: function(tagName) {
18989         var type = tagName.toUpperCase();
18990         this.invalidHandleTypes[type] = type;
18991     },
18992
18993     /**
18994      * Lets you to specify an element id for a child of a drag handle
18995      * that should not initiate a drag
18996      * @method addInvalidHandleId
18997      * @param {string} id the element id of the element you wish to ignore
18998      */
18999     addInvalidHandleId: function(id) {
19000         if (typeof id !== "string") {
19001             id = Roo.id(id);
19002         }
19003         this.invalidHandleIds[id] = id;
19004     },
19005
19006     /**
19007      * Lets you specify a css class of elements that will not initiate a drag
19008      * @method addInvalidHandleClass
19009      * @param {string} cssClass the class of the elements you wish to ignore
19010      */
19011     addInvalidHandleClass: function(cssClass) {
19012         this.invalidHandleClasses.push(cssClass);
19013     },
19014
19015     /**
19016      * Unsets an excluded tag name set by addInvalidHandleType
19017      * @method removeInvalidHandleType
19018      * @param {string} tagName the type of element to unexclude
19019      */
19020     removeInvalidHandleType: function(tagName) {
19021         var type = tagName.toUpperCase();
19022         // this.invalidHandleTypes[type] = null;
19023         delete this.invalidHandleTypes[type];
19024     },
19025
19026     /**
19027      * Unsets an invalid handle id
19028      * @method removeInvalidHandleId
19029      * @param {string} id the id of the element to re-enable
19030      */
19031     removeInvalidHandleId: function(id) {
19032         if (typeof id !== "string") {
19033             id = Roo.id(id);
19034         }
19035         delete this.invalidHandleIds[id];
19036     },
19037
19038     /**
19039      * Unsets an invalid css class
19040      * @method removeInvalidHandleClass
19041      * @param {string} cssClass the class of the element(s) you wish to
19042      * re-enable
19043      */
19044     removeInvalidHandleClass: function(cssClass) {
19045         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19046             if (this.invalidHandleClasses[i] == cssClass) {
19047                 delete this.invalidHandleClasses[i];
19048             }
19049         }
19050     },
19051
19052     /**
19053      * Checks the tag exclusion list to see if this click should be ignored
19054      * @method isValidHandleChild
19055      * @param {HTMLElement} node the HTMLElement to evaluate
19056      * @return {boolean} true if this is a valid tag type, false if not
19057      */
19058     isValidHandleChild: function(node) {
19059
19060         var valid = true;
19061         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19062         var nodeName;
19063         try {
19064             nodeName = node.nodeName.toUpperCase();
19065         } catch(e) {
19066             nodeName = node.nodeName;
19067         }
19068         valid = valid && !this.invalidHandleTypes[nodeName];
19069         valid = valid && !this.invalidHandleIds[node.id];
19070
19071         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19072             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19073         }
19074
19075
19076         return valid;
19077
19078     },
19079
19080     /**
19081      * Create the array of horizontal tick marks if an interval was specified
19082      * in setXConstraint().
19083      * @method setXTicks
19084      * @private
19085      */
19086     setXTicks: function(iStartX, iTickSize) {
19087         this.xTicks = [];
19088         this.xTickSize = iTickSize;
19089
19090         var tickMap = {};
19091
19092         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19093             if (!tickMap[i]) {
19094                 this.xTicks[this.xTicks.length] = i;
19095                 tickMap[i] = true;
19096             }
19097         }
19098
19099         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19100             if (!tickMap[i]) {
19101                 this.xTicks[this.xTicks.length] = i;
19102                 tickMap[i] = true;
19103             }
19104         }
19105
19106         this.xTicks.sort(this.DDM.numericSort) ;
19107     },
19108
19109     /**
19110      * Create the array of vertical tick marks if an interval was specified in
19111      * setYConstraint().
19112      * @method setYTicks
19113      * @private
19114      */
19115     setYTicks: function(iStartY, iTickSize) {
19116         this.yTicks = [];
19117         this.yTickSize = iTickSize;
19118
19119         var tickMap = {};
19120
19121         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19122             if (!tickMap[i]) {
19123                 this.yTicks[this.yTicks.length] = i;
19124                 tickMap[i] = true;
19125             }
19126         }
19127
19128         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19129             if (!tickMap[i]) {
19130                 this.yTicks[this.yTicks.length] = i;
19131                 tickMap[i] = true;
19132             }
19133         }
19134
19135         this.yTicks.sort(this.DDM.numericSort) ;
19136     },
19137
19138     /**
19139      * By default, the element can be dragged any place on the screen.  Use
19140      * this method to limit the horizontal travel of the element.  Pass in
19141      * 0,0 for the parameters if you want to lock the drag to the y axis.
19142      * @method setXConstraint
19143      * @param {int} iLeft the number of pixels the element can move to the left
19144      * @param {int} iRight the number of pixels the element can move to the
19145      * right
19146      * @param {int} iTickSize optional parameter for specifying that the
19147      * element
19148      * should move iTickSize pixels at a time.
19149      */
19150     setXConstraint: function(iLeft, iRight, iTickSize) {
19151         this.leftConstraint = iLeft;
19152         this.rightConstraint = iRight;
19153
19154         this.minX = this.initPageX - iLeft;
19155         this.maxX = this.initPageX + iRight;
19156         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19157
19158         this.constrainX = true;
19159     },
19160
19161     /**
19162      * Clears any constraints applied to this instance.  Also clears ticks
19163      * since they can't exist independent of a constraint at this time.
19164      * @method clearConstraints
19165      */
19166     clearConstraints: function() {
19167         this.constrainX = false;
19168         this.constrainY = false;
19169         this.clearTicks();
19170     },
19171
19172     /**
19173      * Clears any tick interval defined for this instance
19174      * @method clearTicks
19175      */
19176     clearTicks: function() {
19177         this.xTicks = null;
19178         this.yTicks = null;
19179         this.xTickSize = 0;
19180         this.yTickSize = 0;
19181     },
19182
19183     /**
19184      * By default, the element can be dragged any place on the screen.  Set
19185      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19186      * parameters if you want to lock the drag to the x axis.
19187      * @method setYConstraint
19188      * @param {int} iUp the number of pixels the element can move up
19189      * @param {int} iDown the number of pixels the element can move down
19190      * @param {int} iTickSize optional parameter for specifying that the
19191      * element should move iTickSize pixels at a time.
19192      */
19193     setYConstraint: function(iUp, iDown, iTickSize) {
19194         this.topConstraint = iUp;
19195         this.bottomConstraint = iDown;
19196
19197         this.minY = this.initPageY - iUp;
19198         this.maxY = this.initPageY + iDown;
19199         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19200
19201         this.constrainY = true;
19202
19203     },
19204
19205     /**
19206      * resetConstraints must be called if you manually reposition a dd element.
19207      * @method resetConstraints
19208      * @param {boolean} maintainOffset
19209      */
19210     resetConstraints: function() {
19211
19212
19213         // Maintain offsets if necessary
19214         if (this.initPageX || this.initPageX === 0) {
19215             // figure out how much this thing has moved
19216             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19217             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19218
19219             this.setInitPosition(dx, dy);
19220
19221         // This is the first time we have detected the element's position
19222         } else {
19223             this.setInitPosition();
19224         }
19225
19226         if (this.constrainX) {
19227             this.setXConstraint( this.leftConstraint,
19228                                  this.rightConstraint,
19229                                  this.xTickSize        );
19230         }
19231
19232         if (this.constrainY) {
19233             this.setYConstraint( this.topConstraint,
19234                                  this.bottomConstraint,
19235                                  this.yTickSize         );
19236         }
19237     },
19238
19239     /**
19240      * Normally the drag element is moved pixel by pixel, but we can specify
19241      * that it move a number of pixels at a time.  This method resolves the
19242      * location when we have it set up like this.
19243      * @method getTick
19244      * @param {int} val where we want to place the object
19245      * @param {int[]} tickArray sorted array of valid points
19246      * @return {int} the closest tick
19247      * @private
19248      */
19249     getTick: function(val, tickArray) {
19250
19251         if (!tickArray) {
19252             // If tick interval is not defined, it is effectively 1 pixel,
19253             // so we return the value passed to us.
19254             return val;
19255         } else if (tickArray[0] >= val) {
19256             // The value is lower than the first tick, so we return the first
19257             // tick.
19258             return tickArray[0];
19259         } else {
19260             for (var i=0, len=tickArray.length; i<len; ++i) {
19261                 var next = i + 1;
19262                 if (tickArray[next] && tickArray[next] >= val) {
19263                     var diff1 = val - tickArray[i];
19264                     var diff2 = tickArray[next] - val;
19265                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19266                 }
19267             }
19268
19269             // The value is larger than the last tick, so we return the last
19270             // tick.
19271             return tickArray[tickArray.length - 1];
19272         }
19273     },
19274
19275     /**
19276      * toString method
19277      * @method toString
19278      * @return {string} string representation of the dd obj
19279      */
19280     toString: function() {
19281         return ("DragDrop " + this.id);
19282     }
19283
19284 });
19285
19286 })();
19287 /*
19288  * Based on:
19289  * Ext JS Library 1.1.1
19290  * Copyright(c) 2006-2007, Ext JS, LLC.
19291  *
19292  * Originally Released Under LGPL - original licence link has changed is not relivant.
19293  *
19294  * Fork - LGPL
19295  * <script type="text/javascript">
19296  */
19297
19298
19299 /**
19300  * The drag and drop utility provides a framework for building drag and drop
19301  * applications.  In addition to enabling drag and drop for specific elements,
19302  * the drag and drop elements are tracked by the manager class, and the
19303  * interactions between the various elements are tracked during the drag and
19304  * the implementing code is notified about these important moments.
19305  */
19306
19307 // Only load the library once.  Rewriting the manager class would orphan
19308 // existing drag and drop instances.
19309 if (!Roo.dd.DragDropMgr) {
19310
19311 /**
19312  * @class Roo.dd.DragDropMgr
19313  * DragDropMgr is a singleton that tracks the element interaction for
19314  * all DragDrop items in the window.  Generally, you will not call
19315  * this class directly, but it does have helper methods that could
19316  * be useful in your DragDrop implementations.
19317  * @singleton
19318  */
19319 Roo.dd.DragDropMgr = function() {
19320
19321     var Event = Roo.EventManager;
19322
19323     return {
19324
19325         /**
19326          * Two dimensional Array of registered DragDrop objects.  The first
19327          * dimension is the DragDrop item group, the second the DragDrop
19328          * object.
19329          * @property ids
19330          * @type {string: string}
19331          * @private
19332          * @static
19333          */
19334         ids: {},
19335
19336         /**
19337          * Array of element ids defined as drag handles.  Used to determine
19338          * if the element that generated the mousedown event is actually the
19339          * handle and not the html element itself.
19340          * @property handleIds
19341          * @type {string: string}
19342          * @private
19343          * @static
19344          */
19345         handleIds: {},
19346
19347         /**
19348          * the DragDrop object that is currently being dragged
19349          * @property dragCurrent
19350          * @type DragDrop
19351          * @private
19352          * @static
19353          **/
19354         dragCurrent: null,
19355
19356         /**
19357          * the DragDrop object(s) that are being hovered over
19358          * @property dragOvers
19359          * @type Array
19360          * @private
19361          * @static
19362          */
19363         dragOvers: {},
19364
19365         /**
19366          * the X distance between the cursor and the object being dragged
19367          * @property deltaX
19368          * @type int
19369          * @private
19370          * @static
19371          */
19372         deltaX: 0,
19373
19374         /**
19375          * the Y distance between the cursor and the object being dragged
19376          * @property deltaY
19377          * @type int
19378          * @private
19379          * @static
19380          */
19381         deltaY: 0,
19382
19383         /**
19384          * Flag to determine if we should prevent the default behavior of the
19385          * events we define. By default this is true, but this can be set to
19386          * false if you need the default behavior (not recommended)
19387          * @property preventDefault
19388          * @type boolean
19389          * @static
19390          */
19391         preventDefault: true,
19392
19393         /**
19394          * Flag to determine if we should stop the propagation of the events
19395          * we generate. This is true by default but you may want to set it to
19396          * false if the html element contains other features that require the
19397          * mouse click.
19398          * @property stopPropagation
19399          * @type boolean
19400          * @static
19401          */
19402         stopPropagation: true,
19403
19404         /**
19405          * Internal flag that is set to true when drag and drop has been
19406          * intialized
19407          * @property initialized
19408          * @private
19409          * @static
19410          */
19411         initalized: false,
19412
19413         /**
19414          * All drag and drop can be disabled.
19415          * @property locked
19416          * @private
19417          * @static
19418          */
19419         locked: false,
19420
19421         /**
19422          * Called the first time an element is registered.
19423          * @method init
19424          * @private
19425          * @static
19426          */
19427         init: function() {
19428             this.initialized = true;
19429         },
19430
19431         /**
19432          * In point mode, drag and drop interaction is defined by the
19433          * location of the cursor during the drag/drop
19434          * @property POINT
19435          * @type int
19436          * @static
19437          */
19438         POINT: 0,
19439
19440         /**
19441          * In intersect mode, drag and drop interactio nis defined by the
19442          * overlap of two or more drag and drop objects.
19443          * @property INTERSECT
19444          * @type int
19445          * @static
19446          */
19447         INTERSECT: 1,
19448
19449         /**
19450          * The current drag and drop mode.  Default: POINT
19451          * @property mode
19452          * @type int
19453          * @static
19454          */
19455         mode: 0,
19456
19457         /**
19458          * Runs method on all drag and drop objects
19459          * @method _execOnAll
19460          * @private
19461          * @static
19462          */
19463         _execOnAll: function(sMethod, args) {
19464             for (var i in this.ids) {
19465                 for (var j in this.ids[i]) {
19466                     var oDD = this.ids[i][j];
19467                     if (! this.isTypeOfDD(oDD)) {
19468                         continue;
19469                     }
19470                     oDD[sMethod].apply(oDD, args);
19471                 }
19472             }
19473         },
19474
19475         /**
19476          * Drag and drop initialization.  Sets up the global event handlers
19477          * @method _onLoad
19478          * @private
19479          * @static
19480          */
19481         _onLoad: function() {
19482
19483             this.init();
19484
19485             if (!Roo.isTouch) {
19486                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19487                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19488             }
19489             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19490             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19491             
19492             Event.on(window,   "unload",    this._onUnload, this, true);
19493             Event.on(window,   "resize",    this._onResize, this, true);
19494             // Event.on(window,   "mouseout",    this._test);
19495
19496         },
19497
19498         /**
19499          * Reset constraints on all drag and drop objs
19500          * @method _onResize
19501          * @private
19502          * @static
19503          */
19504         _onResize: function(e) {
19505             this._execOnAll("resetConstraints", []);
19506         },
19507
19508         /**
19509          * Lock all drag and drop functionality
19510          * @method lock
19511          * @static
19512          */
19513         lock: function() { this.locked = true; },
19514
19515         /**
19516          * Unlock all drag and drop functionality
19517          * @method unlock
19518          * @static
19519          */
19520         unlock: function() { this.locked = false; },
19521
19522         /**
19523          * Is drag and drop locked?
19524          * @method isLocked
19525          * @return {boolean} True if drag and drop is locked, false otherwise.
19526          * @static
19527          */
19528         isLocked: function() { return this.locked; },
19529
19530         /**
19531          * Location cache that is set for all drag drop objects when a drag is
19532          * initiated, cleared when the drag is finished.
19533          * @property locationCache
19534          * @private
19535          * @static
19536          */
19537         locationCache: {},
19538
19539         /**
19540          * Set useCache to false if you want to force object the lookup of each
19541          * drag and drop linked element constantly during a drag.
19542          * @property useCache
19543          * @type boolean
19544          * @static
19545          */
19546         useCache: true,
19547
19548         /**
19549          * The number of pixels that the mouse needs to move after the
19550          * mousedown before the drag is initiated.  Default=3;
19551          * @property clickPixelThresh
19552          * @type int
19553          * @static
19554          */
19555         clickPixelThresh: 3,
19556
19557         /**
19558          * The number of milliseconds after the mousedown event to initiate the
19559          * drag if we don't get a mouseup event. Default=1000
19560          * @property clickTimeThresh
19561          * @type int
19562          * @static
19563          */
19564         clickTimeThresh: 350,
19565
19566         /**
19567          * Flag that indicates that either the drag pixel threshold or the
19568          * mousdown time threshold has been met
19569          * @property dragThreshMet
19570          * @type boolean
19571          * @private
19572          * @static
19573          */
19574         dragThreshMet: false,
19575
19576         /**
19577          * Timeout used for the click time threshold
19578          * @property clickTimeout
19579          * @type Object
19580          * @private
19581          * @static
19582          */
19583         clickTimeout: null,
19584
19585         /**
19586          * The X position of the mousedown event stored for later use when a
19587          * drag threshold is met.
19588          * @property startX
19589          * @type int
19590          * @private
19591          * @static
19592          */
19593         startX: 0,
19594
19595         /**
19596          * The Y position of the mousedown event stored for later use when a
19597          * drag threshold is met.
19598          * @property startY
19599          * @type int
19600          * @private
19601          * @static
19602          */
19603         startY: 0,
19604
19605         /**
19606          * Each DragDrop instance must be registered with the DragDropMgr.
19607          * This is executed in DragDrop.init()
19608          * @method regDragDrop
19609          * @param {DragDrop} oDD the DragDrop object to register
19610          * @param {String} sGroup the name of the group this element belongs to
19611          * @static
19612          */
19613         regDragDrop: function(oDD, sGroup) {
19614             if (!this.initialized) { this.init(); }
19615
19616             if (!this.ids[sGroup]) {
19617                 this.ids[sGroup] = {};
19618             }
19619             this.ids[sGroup][oDD.id] = oDD;
19620         },
19621
19622         /**
19623          * Removes the supplied dd instance from the supplied group. Executed
19624          * by DragDrop.removeFromGroup, so don't call this function directly.
19625          * @method removeDDFromGroup
19626          * @private
19627          * @static
19628          */
19629         removeDDFromGroup: function(oDD, sGroup) {
19630             if (!this.ids[sGroup]) {
19631                 this.ids[sGroup] = {};
19632             }
19633
19634             var obj = this.ids[sGroup];
19635             if (obj && obj[oDD.id]) {
19636                 delete obj[oDD.id];
19637             }
19638         },
19639
19640         /**
19641          * Unregisters a drag and drop item.  This is executed in
19642          * DragDrop.unreg, use that method instead of calling this directly.
19643          * @method _remove
19644          * @private
19645          * @static
19646          */
19647         _remove: function(oDD) {
19648             for (var g in oDD.groups) {
19649                 if (g && this.ids[g][oDD.id]) {
19650                     delete this.ids[g][oDD.id];
19651                 }
19652             }
19653             delete this.handleIds[oDD.id];
19654         },
19655
19656         /**
19657          * Each DragDrop handle element must be registered.  This is done
19658          * automatically when executing DragDrop.setHandleElId()
19659          * @method regHandle
19660          * @param {String} sDDId the DragDrop id this element is a handle for
19661          * @param {String} sHandleId the id of the element that is the drag
19662          * handle
19663          * @static
19664          */
19665         regHandle: function(sDDId, sHandleId) {
19666             if (!this.handleIds[sDDId]) {
19667                 this.handleIds[sDDId] = {};
19668             }
19669             this.handleIds[sDDId][sHandleId] = sHandleId;
19670         },
19671
19672         /**
19673          * Utility function to determine if a given element has been
19674          * registered as a drag drop item.
19675          * @method isDragDrop
19676          * @param {String} id the element id to check
19677          * @return {boolean} true if this element is a DragDrop item,
19678          * false otherwise
19679          * @static
19680          */
19681         isDragDrop: function(id) {
19682             return ( this.getDDById(id) ) ? true : false;
19683         },
19684
19685         /**
19686          * Returns the drag and drop instances that are in all groups the
19687          * passed in instance belongs to.
19688          * @method getRelated
19689          * @param {DragDrop} p_oDD the obj to get related data for
19690          * @param {boolean} bTargetsOnly if true, only return targetable objs
19691          * @return {DragDrop[]} the related instances
19692          * @static
19693          */
19694         getRelated: function(p_oDD, bTargetsOnly) {
19695             var oDDs = [];
19696             for (var i in p_oDD.groups) {
19697                 for (j in this.ids[i]) {
19698                     var dd = this.ids[i][j];
19699                     if (! this.isTypeOfDD(dd)) {
19700                         continue;
19701                     }
19702                     if (!bTargetsOnly || dd.isTarget) {
19703                         oDDs[oDDs.length] = dd;
19704                     }
19705                 }
19706             }
19707
19708             return oDDs;
19709         },
19710
19711         /**
19712          * Returns true if the specified dd target is a legal target for
19713          * the specifice drag obj
19714          * @method isLegalTarget
19715          * @param {DragDrop} the drag obj
19716          * @param {DragDrop} the target
19717          * @return {boolean} true if the target is a legal target for the
19718          * dd obj
19719          * @static
19720          */
19721         isLegalTarget: function (oDD, oTargetDD) {
19722             var targets = this.getRelated(oDD, true);
19723             for (var i=0, len=targets.length;i<len;++i) {
19724                 if (targets[i].id == oTargetDD.id) {
19725                     return true;
19726                 }
19727             }
19728
19729             return false;
19730         },
19731
19732         /**
19733          * My goal is to be able to transparently determine if an object is
19734          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19735          * returns "object", oDD.constructor.toString() always returns
19736          * "DragDrop" and not the name of the subclass.  So for now it just
19737          * evaluates a well-known variable in DragDrop.
19738          * @method isTypeOfDD
19739          * @param {Object} the object to evaluate
19740          * @return {boolean} true if typeof oDD = DragDrop
19741          * @static
19742          */
19743         isTypeOfDD: function (oDD) {
19744             return (oDD && oDD.__ygDragDrop);
19745         },
19746
19747         /**
19748          * Utility function to determine if a given element has been
19749          * registered as a drag drop handle for the given Drag Drop object.
19750          * @method isHandle
19751          * @param {String} id the element id to check
19752          * @return {boolean} true if this element is a DragDrop handle, false
19753          * otherwise
19754          * @static
19755          */
19756         isHandle: function(sDDId, sHandleId) {
19757             return ( this.handleIds[sDDId] &&
19758                             this.handleIds[sDDId][sHandleId] );
19759         },
19760
19761         /**
19762          * Returns the DragDrop instance for a given id
19763          * @method getDDById
19764          * @param {String} id the id of the DragDrop object
19765          * @return {DragDrop} the drag drop object, null if it is not found
19766          * @static
19767          */
19768         getDDById: function(id) {
19769             for (var i in this.ids) {
19770                 if (this.ids[i][id]) {
19771                     return this.ids[i][id];
19772                 }
19773             }
19774             return null;
19775         },
19776
19777         /**
19778          * Fired after a registered DragDrop object gets the mousedown event.
19779          * Sets up the events required to track the object being dragged
19780          * @method handleMouseDown
19781          * @param {Event} e the event
19782          * @param oDD the DragDrop object being dragged
19783          * @private
19784          * @static
19785          */
19786         handleMouseDown: function(e, oDD) {
19787             if(Roo.QuickTips){
19788                 Roo.QuickTips.disable();
19789             }
19790             this.currentTarget = e.getTarget();
19791
19792             this.dragCurrent = oDD;
19793
19794             var el = oDD.getEl();
19795
19796             // track start position
19797             this.startX = e.getPageX();
19798             this.startY = e.getPageY();
19799
19800             this.deltaX = this.startX - el.offsetLeft;
19801             this.deltaY = this.startY - el.offsetTop;
19802
19803             this.dragThreshMet = false;
19804
19805             this.clickTimeout = setTimeout(
19806                     function() {
19807                         var DDM = Roo.dd.DDM;
19808                         DDM.startDrag(DDM.startX, DDM.startY);
19809                     },
19810                     this.clickTimeThresh );
19811         },
19812
19813         /**
19814          * Fired when either the drag pixel threshol or the mousedown hold
19815          * time threshold has been met.
19816          * @method startDrag
19817          * @param x {int} the X position of the original mousedown
19818          * @param y {int} the Y position of the original mousedown
19819          * @static
19820          */
19821         startDrag: function(x, y) {
19822             clearTimeout(this.clickTimeout);
19823             if (this.dragCurrent) {
19824                 this.dragCurrent.b4StartDrag(x, y);
19825                 this.dragCurrent.startDrag(x, y);
19826             }
19827             this.dragThreshMet = true;
19828         },
19829
19830         /**
19831          * Internal function to handle the mouseup event.  Will be invoked
19832          * from the context of the document.
19833          * @method handleMouseUp
19834          * @param {Event} e the event
19835          * @private
19836          * @static
19837          */
19838         handleMouseUp: function(e) {
19839
19840             if(Roo.QuickTips){
19841                 Roo.QuickTips.enable();
19842             }
19843             if (! this.dragCurrent) {
19844                 return;
19845             }
19846
19847             clearTimeout(this.clickTimeout);
19848
19849             if (this.dragThreshMet) {
19850                 this.fireEvents(e, true);
19851             } else {
19852             }
19853
19854             this.stopDrag(e);
19855
19856             this.stopEvent(e);
19857         },
19858
19859         /**
19860          * Utility to stop event propagation and event default, if these
19861          * features are turned on.
19862          * @method stopEvent
19863          * @param {Event} e the event as returned by this.getEvent()
19864          * @static
19865          */
19866         stopEvent: function(e){
19867             if(this.stopPropagation) {
19868                 e.stopPropagation();
19869             }
19870
19871             if (this.preventDefault) {
19872                 e.preventDefault();
19873             }
19874         },
19875
19876         /**
19877          * Internal function to clean up event handlers after the drag
19878          * operation is complete
19879          * @method stopDrag
19880          * @param {Event} e the event
19881          * @private
19882          * @static
19883          */
19884         stopDrag: function(e) {
19885             // Fire the drag end event for the item that was dragged
19886             if (this.dragCurrent) {
19887                 if (this.dragThreshMet) {
19888                     this.dragCurrent.b4EndDrag(e);
19889                     this.dragCurrent.endDrag(e);
19890                 }
19891
19892                 this.dragCurrent.onMouseUp(e);
19893             }
19894
19895             this.dragCurrent = null;
19896             this.dragOvers = {};
19897         },
19898
19899         /**
19900          * Internal function to handle the mousemove event.  Will be invoked
19901          * from the context of the html element.
19902          *
19903          * @TODO figure out what we can do about mouse events lost when the
19904          * user drags objects beyond the window boundary.  Currently we can
19905          * detect this in internet explorer by verifying that the mouse is
19906          * down during the mousemove event.  Firefox doesn't give us the
19907          * button state on the mousemove event.
19908          * @method handleMouseMove
19909          * @param {Event} e the event
19910          * @private
19911          * @static
19912          */
19913         handleMouseMove: function(e) {
19914             if (! this.dragCurrent) {
19915                 return true;
19916             }
19917
19918             // var button = e.which || e.button;
19919
19920             // check for IE mouseup outside of page boundary
19921             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19922                 this.stopEvent(e);
19923                 return this.handleMouseUp(e);
19924             }
19925
19926             if (!this.dragThreshMet) {
19927                 var diffX = Math.abs(this.startX - e.getPageX());
19928                 var diffY = Math.abs(this.startY - e.getPageY());
19929                 if (diffX > this.clickPixelThresh ||
19930                             diffY > this.clickPixelThresh) {
19931                     this.startDrag(this.startX, this.startY);
19932                 }
19933             }
19934
19935             if (this.dragThreshMet) {
19936                 this.dragCurrent.b4Drag(e);
19937                 this.dragCurrent.onDrag(e);
19938                 if(!this.dragCurrent.moveOnly){
19939                     this.fireEvents(e, false);
19940                 }
19941             }
19942
19943             this.stopEvent(e);
19944
19945             return true;
19946         },
19947
19948         /**
19949          * Iterates over all of the DragDrop elements to find ones we are
19950          * hovering over or dropping on
19951          * @method fireEvents
19952          * @param {Event} e the event
19953          * @param {boolean} isDrop is this a drop op or a mouseover op?
19954          * @private
19955          * @static
19956          */
19957         fireEvents: function(e, isDrop) {
19958             var dc = this.dragCurrent;
19959
19960             // If the user did the mouse up outside of the window, we could
19961             // get here even though we have ended the drag.
19962             if (!dc || dc.isLocked()) {
19963                 return;
19964             }
19965
19966             var pt = e.getPoint();
19967
19968             // cache the previous dragOver array
19969             var oldOvers = [];
19970
19971             var outEvts   = [];
19972             var overEvts  = [];
19973             var dropEvts  = [];
19974             var enterEvts = [];
19975
19976             // Check to see if the object(s) we were hovering over is no longer
19977             // being hovered over so we can fire the onDragOut event
19978             for (var i in this.dragOvers) {
19979
19980                 var ddo = this.dragOvers[i];
19981
19982                 if (! this.isTypeOfDD(ddo)) {
19983                     continue;
19984                 }
19985
19986                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19987                     outEvts.push( ddo );
19988                 }
19989
19990                 oldOvers[i] = true;
19991                 delete this.dragOvers[i];
19992             }
19993
19994             for (var sGroup in dc.groups) {
19995
19996                 if ("string" != typeof sGroup) {
19997                     continue;
19998                 }
19999
20000                 for (i in this.ids[sGroup]) {
20001                     var oDD = this.ids[sGroup][i];
20002                     if (! this.isTypeOfDD(oDD)) {
20003                         continue;
20004                     }
20005
20006                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20007                         if (this.isOverTarget(pt, oDD, this.mode)) {
20008                             // look for drop interactions
20009                             if (isDrop) {
20010                                 dropEvts.push( oDD );
20011                             // look for drag enter and drag over interactions
20012                             } else {
20013
20014                                 // initial drag over: dragEnter fires
20015                                 if (!oldOvers[oDD.id]) {
20016                                     enterEvts.push( oDD );
20017                                 // subsequent drag overs: dragOver fires
20018                                 } else {
20019                                     overEvts.push( oDD );
20020                                 }
20021
20022                                 this.dragOvers[oDD.id] = oDD;
20023                             }
20024                         }
20025                     }
20026                 }
20027             }
20028
20029             if (this.mode) {
20030                 if (outEvts.length) {
20031                     dc.b4DragOut(e, outEvts);
20032                     dc.onDragOut(e, outEvts);
20033                 }
20034
20035                 if (enterEvts.length) {
20036                     dc.onDragEnter(e, enterEvts);
20037                 }
20038
20039                 if (overEvts.length) {
20040                     dc.b4DragOver(e, overEvts);
20041                     dc.onDragOver(e, overEvts);
20042                 }
20043
20044                 if (dropEvts.length) {
20045                     dc.b4DragDrop(e, dropEvts);
20046                     dc.onDragDrop(e, dropEvts);
20047                 }
20048
20049             } else {
20050                 // fire dragout events
20051                 var len = 0;
20052                 for (i=0, len=outEvts.length; i<len; ++i) {
20053                     dc.b4DragOut(e, outEvts[i].id);
20054                     dc.onDragOut(e, outEvts[i].id);
20055                 }
20056
20057                 // fire enter events
20058                 for (i=0,len=enterEvts.length; i<len; ++i) {
20059                     // dc.b4DragEnter(e, oDD.id);
20060                     dc.onDragEnter(e, enterEvts[i].id);
20061                 }
20062
20063                 // fire over events
20064                 for (i=0,len=overEvts.length; i<len; ++i) {
20065                     dc.b4DragOver(e, overEvts[i].id);
20066                     dc.onDragOver(e, overEvts[i].id);
20067                 }
20068
20069                 // fire drop events
20070                 for (i=0, len=dropEvts.length; i<len; ++i) {
20071                     dc.b4DragDrop(e, dropEvts[i].id);
20072                     dc.onDragDrop(e, dropEvts[i].id);
20073                 }
20074
20075             }
20076
20077             // notify about a drop that did not find a target
20078             if (isDrop && !dropEvts.length) {
20079                 dc.onInvalidDrop(e);
20080             }
20081
20082         },
20083
20084         /**
20085          * Helper function for getting the best match from the list of drag
20086          * and drop objects returned by the drag and drop events when we are
20087          * in INTERSECT mode.  It returns either the first object that the
20088          * cursor is over, or the object that has the greatest overlap with
20089          * the dragged element.
20090          * @method getBestMatch
20091          * @param  {DragDrop[]} dds The array of drag and drop objects
20092          * targeted
20093          * @return {DragDrop}       The best single match
20094          * @static
20095          */
20096         getBestMatch: function(dds) {
20097             var winner = null;
20098             // Return null if the input is not what we expect
20099             //if (!dds || !dds.length || dds.length == 0) {
20100                // winner = null;
20101             // If there is only one item, it wins
20102             //} else if (dds.length == 1) {
20103
20104             var len = dds.length;
20105
20106             if (len == 1) {
20107                 winner = dds[0];
20108             } else {
20109                 // Loop through the targeted items
20110                 for (var i=0; i<len; ++i) {
20111                     var dd = dds[i];
20112                     // If the cursor is over the object, it wins.  If the
20113                     // cursor is over multiple matches, the first one we come
20114                     // to wins.
20115                     if (dd.cursorIsOver) {
20116                         winner = dd;
20117                         break;
20118                     // Otherwise the object with the most overlap wins
20119                     } else {
20120                         if (!winner ||
20121                             winner.overlap.getArea() < dd.overlap.getArea()) {
20122                             winner = dd;
20123                         }
20124                     }
20125                 }
20126             }
20127
20128             return winner;
20129         },
20130
20131         /**
20132          * Refreshes the cache of the top-left and bottom-right points of the
20133          * drag and drop objects in the specified group(s).  This is in the
20134          * format that is stored in the drag and drop instance, so typical
20135          * usage is:
20136          * <code>
20137          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20138          * </code>
20139          * Alternatively:
20140          * <code>
20141          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20142          * </code>
20143          * @TODO this really should be an indexed array.  Alternatively this
20144          * method could accept both.
20145          * @method refreshCache
20146          * @param {Object} groups an associative array of groups to refresh
20147          * @static
20148          */
20149         refreshCache: function(groups) {
20150             for (var sGroup in groups) {
20151                 if ("string" != typeof sGroup) {
20152                     continue;
20153                 }
20154                 for (var i in this.ids[sGroup]) {
20155                     var oDD = this.ids[sGroup][i];
20156
20157                     if (this.isTypeOfDD(oDD)) {
20158                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20159                         var loc = this.getLocation(oDD);
20160                         if (loc) {
20161                             this.locationCache[oDD.id] = loc;
20162                         } else {
20163                             delete this.locationCache[oDD.id];
20164                             // this will unregister the drag and drop object if
20165                             // the element is not in a usable state
20166                             // oDD.unreg();
20167                         }
20168                     }
20169                 }
20170             }
20171         },
20172
20173         /**
20174          * This checks to make sure an element exists and is in the DOM.  The
20175          * main purpose is to handle cases where innerHTML is used to remove
20176          * drag and drop objects from the DOM.  IE provides an 'unspecified
20177          * error' when trying to access the offsetParent of such an element
20178          * @method verifyEl
20179          * @param {HTMLElement} el the element to check
20180          * @return {boolean} true if the element looks usable
20181          * @static
20182          */
20183         verifyEl: function(el) {
20184             if (el) {
20185                 var parent;
20186                 if(Roo.isIE){
20187                     try{
20188                         parent = el.offsetParent;
20189                     }catch(e){}
20190                 }else{
20191                     parent = el.offsetParent;
20192                 }
20193                 if (parent) {
20194                     return true;
20195                 }
20196             }
20197
20198             return false;
20199         },
20200
20201         /**
20202          * Returns a Region object containing the drag and drop element's position
20203          * and size, including the padding configured for it
20204          * @method getLocation
20205          * @param {DragDrop} oDD the drag and drop object to get the
20206          *                       location for
20207          * @return {Roo.lib.Region} a Region object representing the total area
20208          *                             the element occupies, including any padding
20209          *                             the instance is configured for.
20210          * @static
20211          */
20212         getLocation: function(oDD) {
20213             if (! this.isTypeOfDD(oDD)) {
20214                 return null;
20215             }
20216
20217             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20218
20219             try {
20220                 pos= Roo.lib.Dom.getXY(el);
20221             } catch (e) { }
20222
20223             if (!pos) {
20224                 return null;
20225             }
20226
20227             x1 = pos[0];
20228             x2 = x1 + el.offsetWidth;
20229             y1 = pos[1];
20230             y2 = y1 + el.offsetHeight;
20231
20232             t = y1 - oDD.padding[0];
20233             r = x2 + oDD.padding[1];
20234             b = y2 + oDD.padding[2];
20235             l = x1 - oDD.padding[3];
20236
20237             return new Roo.lib.Region( t, r, b, l );
20238         },
20239
20240         /**
20241          * Checks the cursor location to see if it over the target
20242          * @method isOverTarget
20243          * @param {Roo.lib.Point} pt The point to evaluate
20244          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20245          * @return {boolean} true if the mouse is over the target
20246          * @private
20247          * @static
20248          */
20249         isOverTarget: function(pt, oTarget, intersect) {
20250             // use cache if available
20251             var loc = this.locationCache[oTarget.id];
20252             if (!loc || !this.useCache) {
20253                 loc = this.getLocation(oTarget);
20254                 this.locationCache[oTarget.id] = loc;
20255
20256             }
20257
20258             if (!loc) {
20259                 return false;
20260             }
20261
20262             oTarget.cursorIsOver = loc.contains( pt );
20263
20264             // DragDrop is using this as a sanity check for the initial mousedown
20265             // in this case we are done.  In POINT mode, if the drag obj has no
20266             // contraints, we are also done. Otherwise we need to evaluate the
20267             // location of the target as related to the actual location of the
20268             // dragged element.
20269             var dc = this.dragCurrent;
20270             if (!dc || !dc.getTargetCoord ||
20271                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20272                 return oTarget.cursorIsOver;
20273             }
20274
20275             oTarget.overlap = null;
20276
20277             // Get the current location of the drag element, this is the
20278             // location of the mouse event less the delta that represents
20279             // where the original mousedown happened on the element.  We
20280             // need to consider constraints and ticks as well.
20281             var pos = dc.getTargetCoord(pt.x, pt.y);
20282
20283             var el = dc.getDragEl();
20284             var curRegion = new Roo.lib.Region( pos.y,
20285                                                    pos.x + el.offsetWidth,
20286                                                    pos.y + el.offsetHeight,
20287                                                    pos.x );
20288
20289             var overlap = curRegion.intersect(loc);
20290
20291             if (overlap) {
20292                 oTarget.overlap = overlap;
20293                 return (intersect) ? true : oTarget.cursorIsOver;
20294             } else {
20295                 return false;
20296             }
20297         },
20298
20299         /**
20300          * unload event handler
20301          * @method _onUnload
20302          * @private
20303          * @static
20304          */
20305         _onUnload: function(e, me) {
20306             Roo.dd.DragDropMgr.unregAll();
20307         },
20308
20309         /**
20310          * Cleans up the drag and drop events and objects.
20311          * @method unregAll
20312          * @private
20313          * @static
20314          */
20315         unregAll: function() {
20316
20317             if (this.dragCurrent) {
20318                 this.stopDrag();
20319                 this.dragCurrent = null;
20320             }
20321
20322             this._execOnAll("unreg", []);
20323
20324             for (i in this.elementCache) {
20325                 delete this.elementCache[i];
20326             }
20327
20328             this.elementCache = {};
20329             this.ids = {};
20330         },
20331
20332         /**
20333          * A cache of DOM elements
20334          * @property elementCache
20335          * @private
20336          * @static
20337          */
20338         elementCache: {},
20339
20340         /**
20341          * Get the wrapper for the DOM element specified
20342          * @method getElWrapper
20343          * @param {String} id the id of the element to get
20344          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20345          * @private
20346          * @deprecated This wrapper isn't that useful
20347          * @static
20348          */
20349         getElWrapper: function(id) {
20350             var oWrapper = this.elementCache[id];
20351             if (!oWrapper || !oWrapper.el) {
20352                 oWrapper = this.elementCache[id] =
20353                     new this.ElementWrapper(Roo.getDom(id));
20354             }
20355             return oWrapper;
20356         },
20357
20358         /**
20359          * Returns the actual DOM element
20360          * @method getElement
20361          * @param {String} id the id of the elment to get
20362          * @return {Object} The element
20363          * @deprecated use Roo.getDom instead
20364          * @static
20365          */
20366         getElement: function(id) {
20367             return Roo.getDom(id);
20368         },
20369
20370         /**
20371          * Returns the style property for the DOM element (i.e.,
20372          * document.getElById(id).style)
20373          * @method getCss
20374          * @param {String} id the id of the elment to get
20375          * @return {Object} The style property of the element
20376          * @deprecated use Roo.getDom instead
20377          * @static
20378          */
20379         getCss: function(id) {
20380             var el = Roo.getDom(id);
20381             return (el) ? el.style : null;
20382         },
20383
20384         /**
20385          * Inner class for cached elements
20386          * @class DragDropMgr.ElementWrapper
20387          * @for DragDropMgr
20388          * @private
20389          * @deprecated
20390          */
20391         ElementWrapper: function(el) {
20392                 /**
20393                  * The element
20394                  * @property el
20395                  */
20396                 this.el = el || null;
20397                 /**
20398                  * The element id
20399                  * @property id
20400                  */
20401                 this.id = this.el && el.id;
20402                 /**
20403                  * A reference to the style property
20404                  * @property css
20405                  */
20406                 this.css = this.el && el.style;
20407             },
20408
20409         /**
20410          * Returns the X position of an html element
20411          * @method getPosX
20412          * @param el the element for which to get the position
20413          * @return {int} the X coordinate
20414          * @for DragDropMgr
20415          * @deprecated use Roo.lib.Dom.getX instead
20416          * @static
20417          */
20418         getPosX: function(el) {
20419             return Roo.lib.Dom.getX(el);
20420         },
20421
20422         /**
20423          * Returns the Y position of an html element
20424          * @method getPosY
20425          * @param el the element for which to get the position
20426          * @return {int} the Y coordinate
20427          * @deprecated use Roo.lib.Dom.getY instead
20428          * @static
20429          */
20430         getPosY: function(el) {
20431             return Roo.lib.Dom.getY(el);
20432         },
20433
20434         /**
20435          * Swap two nodes.  In IE, we use the native method, for others we
20436          * emulate the IE behavior
20437          * @method swapNode
20438          * @param n1 the first node to swap
20439          * @param n2 the other node to swap
20440          * @static
20441          */
20442         swapNode: function(n1, n2) {
20443             if (n1.swapNode) {
20444                 n1.swapNode(n2);
20445             } else {
20446                 var p = n2.parentNode;
20447                 var s = n2.nextSibling;
20448
20449                 if (s == n1) {
20450                     p.insertBefore(n1, n2);
20451                 } else if (n2 == n1.nextSibling) {
20452                     p.insertBefore(n2, n1);
20453                 } else {
20454                     n1.parentNode.replaceChild(n2, n1);
20455                     p.insertBefore(n1, s);
20456                 }
20457             }
20458         },
20459
20460         /**
20461          * Returns the current scroll position
20462          * @method getScroll
20463          * @private
20464          * @static
20465          */
20466         getScroll: function () {
20467             var t, l, dde=document.documentElement, db=document.body;
20468             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20469                 t = dde.scrollTop;
20470                 l = dde.scrollLeft;
20471             } else if (db) {
20472                 t = db.scrollTop;
20473                 l = db.scrollLeft;
20474             } else {
20475
20476             }
20477             return { top: t, left: l };
20478         },
20479
20480         /**
20481          * Returns the specified element style property
20482          * @method getStyle
20483          * @param {HTMLElement} el          the element
20484          * @param {string}      styleProp   the style property
20485          * @return {string} The value of the style property
20486          * @deprecated use Roo.lib.Dom.getStyle
20487          * @static
20488          */
20489         getStyle: function(el, styleProp) {
20490             return Roo.fly(el).getStyle(styleProp);
20491         },
20492
20493         /**
20494          * Gets the scrollTop
20495          * @method getScrollTop
20496          * @return {int} the document's scrollTop
20497          * @static
20498          */
20499         getScrollTop: function () { return this.getScroll().top; },
20500
20501         /**
20502          * Gets the scrollLeft
20503          * @method getScrollLeft
20504          * @return {int} the document's scrollTop
20505          * @static
20506          */
20507         getScrollLeft: function () { return this.getScroll().left; },
20508
20509         /**
20510          * Sets the x/y position of an element to the location of the
20511          * target element.
20512          * @method moveToEl
20513          * @param {HTMLElement} moveEl      The element to move
20514          * @param {HTMLElement} targetEl    The position reference element
20515          * @static
20516          */
20517         moveToEl: function (moveEl, targetEl) {
20518             var aCoord = Roo.lib.Dom.getXY(targetEl);
20519             Roo.lib.Dom.setXY(moveEl, aCoord);
20520         },
20521
20522         /**
20523          * Numeric array sort function
20524          * @method numericSort
20525          * @static
20526          */
20527         numericSort: function(a, b) { return (a - b); },
20528
20529         /**
20530          * Internal counter
20531          * @property _timeoutCount
20532          * @private
20533          * @static
20534          */
20535         _timeoutCount: 0,
20536
20537         /**
20538          * Trying to make the load order less important.  Without this we get
20539          * an error if this file is loaded before the Event Utility.
20540          * @method _addListeners
20541          * @private
20542          * @static
20543          */
20544         _addListeners: function() {
20545             var DDM = Roo.dd.DDM;
20546             if ( Roo.lib.Event && document ) {
20547                 DDM._onLoad();
20548             } else {
20549                 if (DDM._timeoutCount > 2000) {
20550                 } else {
20551                     setTimeout(DDM._addListeners, 10);
20552                     if (document && document.body) {
20553                         DDM._timeoutCount += 1;
20554                     }
20555                 }
20556             }
20557         },
20558
20559         /**
20560          * Recursively searches the immediate parent and all child nodes for
20561          * the handle element in order to determine wheter or not it was
20562          * clicked.
20563          * @method handleWasClicked
20564          * @param node the html element to inspect
20565          * @static
20566          */
20567         handleWasClicked: function(node, id) {
20568             if (this.isHandle(id, node.id)) {
20569                 return true;
20570             } else {
20571                 // check to see if this is a text node child of the one we want
20572                 var p = node.parentNode;
20573
20574                 while (p) {
20575                     if (this.isHandle(id, p.id)) {
20576                         return true;
20577                     } else {
20578                         p = p.parentNode;
20579                     }
20580                 }
20581             }
20582
20583             return false;
20584         }
20585
20586     };
20587
20588 }();
20589
20590 // shorter alias, save a few bytes
20591 Roo.dd.DDM = Roo.dd.DragDropMgr;
20592 Roo.dd.DDM._addListeners();
20593
20594 }/*
20595  * Based on:
20596  * Ext JS Library 1.1.1
20597  * Copyright(c) 2006-2007, Ext JS, LLC.
20598  *
20599  * Originally Released Under LGPL - original licence link has changed is not relivant.
20600  *
20601  * Fork - LGPL
20602  * <script type="text/javascript">
20603  */
20604
20605 /**
20606  * @class Roo.dd.DD
20607  * A DragDrop implementation where the linked element follows the
20608  * mouse cursor during a drag.
20609  * @extends Roo.dd.DragDrop
20610  * @constructor
20611  * @param {String} id the id of the linked element
20612  * @param {String} sGroup the group of related DragDrop items
20613  * @param {object} config an object containing configurable attributes
20614  *                Valid properties for DD:
20615  *                    scroll
20616  */
20617 Roo.dd.DD = function(id, sGroup, config) {
20618     if (id) {
20619         this.init(id, sGroup, config);
20620     }
20621 };
20622
20623 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20624
20625     /**
20626      * When set to true, the utility automatically tries to scroll the browser
20627      * window wehn a drag and drop element is dragged near the viewport boundary.
20628      * Defaults to true.
20629      * @property scroll
20630      * @type boolean
20631      */
20632     scroll: true,
20633
20634     /**
20635      * Sets the pointer offset to the distance between the linked element's top
20636      * left corner and the location the element was clicked
20637      * @method autoOffset
20638      * @param {int} iPageX the X coordinate of the click
20639      * @param {int} iPageY the Y coordinate of the click
20640      */
20641     autoOffset: function(iPageX, iPageY) {
20642         var x = iPageX - this.startPageX;
20643         var y = iPageY - this.startPageY;
20644         this.setDelta(x, y);
20645     },
20646
20647     /**
20648      * Sets the pointer offset.  You can call this directly to force the
20649      * offset to be in a particular location (e.g., pass in 0,0 to set it
20650      * to the center of the object)
20651      * @method setDelta
20652      * @param {int} iDeltaX the distance from the left
20653      * @param {int} iDeltaY the distance from the top
20654      */
20655     setDelta: function(iDeltaX, iDeltaY) {
20656         this.deltaX = iDeltaX;
20657         this.deltaY = iDeltaY;
20658     },
20659
20660     /**
20661      * Sets the drag element to the location of the mousedown or click event,
20662      * maintaining the cursor location relative to the location on the element
20663      * that was clicked.  Override this if you want to place the element in a
20664      * location other than where the cursor is.
20665      * @method setDragElPos
20666      * @param {int} iPageX the X coordinate of the mousedown or drag event
20667      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20668      */
20669     setDragElPos: function(iPageX, iPageY) {
20670         // the first time we do this, we are going to check to make sure
20671         // the element has css positioning
20672
20673         var el = this.getDragEl();
20674         this.alignElWithMouse(el, iPageX, iPageY);
20675     },
20676
20677     /**
20678      * Sets the element to the location of the mousedown or click event,
20679      * maintaining the cursor location relative to the location on the element
20680      * that was clicked.  Override this if you want to place the element in a
20681      * location other than where the cursor is.
20682      * @method alignElWithMouse
20683      * @param {HTMLElement} el the element to move
20684      * @param {int} iPageX the X coordinate of the mousedown or drag event
20685      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20686      */
20687     alignElWithMouse: function(el, iPageX, iPageY) {
20688         var oCoord = this.getTargetCoord(iPageX, iPageY);
20689         var fly = el.dom ? el : Roo.fly(el);
20690         if (!this.deltaSetXY) {
20691             var aCoord = [oCoord.x, oCoord.y];
20692             fly.setXY(aCoord);
20693             var newLeft = fly.getLeft(true);
20694             var newTop  = fly.getTop(true);
20695             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20696         } else {
20697             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20698         }
20699
20700         this.cachePosition(oCoord.x, oCoord.y);
20701         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20702         return oCoord;
20703     },
20704
20705     /**
20706      * Saves the most recent position so that we can reset the constraints and
20707      * tick marks on-demand.  We need to know this so that we can calculate the
20708      * number of pixels the element is offset from its original position.
20709      * @method cachePosition
20710      * @param iPageX the current x position (optional, this just makes it so we
20711      * don't have to look it up again)
20712      * @param iPageY the current y position (optional, this just makes it so we
20713      * don't have to look it up again)
20714      */
20715     cachePosition: function(iPageX, iPageY) {
20716         if (iPageX) {
20717             this.lastPageX = iPageX;
20718             this.lastPageY = iPageY;
20719         } else {
20720             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20721             this.lastPageX = aCoord[0];
20722             this.lastPageY = aCoord[1];
20723         }
20724     },
20725
20726     /**
20727      * Auto-scroll the window if the dragged object has been moved beyond the
20728      * visible window boundary.
20729      * @method autoScroll
20730      * @param {int} x the drag element's x position
20731      * @param {int} y the drag element's y position
20732      * @param {int} h the height of the drag element
20733      * @param {int} w the width of the drag element
20734      * @private
20735      */
20736     autoScroll: function(x, y, h, w) {
20737
20738         if (this.scroll) {
20739             // The client height
20740             var clientH = Roo.lib.Dom.getViewWidth();
20741
20742             // The client width
20743             var clientW = Roo.lib.Dom.getViewHeight();
20744
20745             // The amt scrolled down
20746             var st = this.DDM.getScrollTop();
20747
20748             // The amt scrolled right
20749             var sl = this.DDM.getScrollLeft();
20750
20751             // Location of the bottom of the element
20752             var bot = h + y;
20753
20754             // Location of the right of the element
20755             var right = w + x;
20756
20757             // The distance from the cursor to the bottom of the visible area,
20758             // adjusted so that we don't scroll if the cursor is beyond the
20759             // element drag constraints
20760             var toBot = (clientH + st - y - this.deltaY);
20761
20762             // The distance from the cursor to the right of the visible area
20763             var toRight = (clientW + sl - x - this.deltaX);
20764
20765
20766             // How close to the edge the cursor must be before we scroll
20767             // var thresh = (document.all) ? 100 : 40;
20768             var thresh = 40;
20769
20770             // How many pixels to scroll per autoscroll op.  This helps to reduce
20771             // clunky scrolling. IE is more sensitive about this ... it needs this
20772             // value to be higher.
20773             var scrAmt = (document.all) ? 80 : 30;
20774
20775             // Scroll down if we are near the bottom of the visible page and the
20776             // obj extends below the crease
20777             if ( bot > clientH && toBot < thresh ) {
20778                 window.scrollTo(sl, st + scrAmt);
20779             }
20780
20781             // Scroll up if the window is scrolled down and the top of the object
20782             // goes above the top border
20783             if ( y < st && st > 0 && y - st < thresh ) {
20784                 window.scrollTo(sl, st - scrAmt);
20785             }
20786
20787             // Scroll right if the obj is beyond the right border and the cursor is
20788             // near the border.
20789             if ( right > clientW && toRight < thresh ) {
20790                 window.scrollTo(sl + scrAmt, st);
20791             }
20792
20793             // Scroll left if the window has been scrolled to the right and the obj
20794             // extends past the left border
20795             if ( x < sl && sl > 0 && x - sl < thresh ) {
20796                 window.scrollTo(sl - scrAmt, st);
20797             }
20798         }
20799     },
20800
20801     /**
20802      * Finds the location the element should be placed if we want to move
20803      * it to where the mouse location less the click offset would place us.
20804      * @method getTargetCoord
20805      * @param {int} iPageX the X coordinate of the click
20806      * @param {int} iPageY the Y coordinate of the click
20807      * @return an object that contains the coordinates (Object.x and Object.y)
20808      * @private
20809      */
20810     getTargetCoord: function(iPageX, iPageY) {
20811
20812
20813         var x = iPageX - this.deltaX;
20814         var y = iPageY - this.deltaY;
20815
20816         if (this.constrainX) {
20817             if (x < this.minX) { x = this.minX; }
20818             if (x > this.maxX) { x = this.maxX; }
20819         }
20820
20821         if (this.constrainY) {
20822             if (y < this.minY) { y = this.minY; }
20823             if (y > this.maxY) { y = this.maxY; }
20824         }
20825
20826         x = this.getTick(x, this.xTicks);
20827         y = this.getTick(y, this.yTicks);
20828
20829
20830         return {x:x, y:y};
20831     },
20832
20833     /*
20834      * Sets up config options specific to this class. Overrides
20835      * Roo.dd.DragDrop, but all versions of this method through the
20836      * inheritance chain are called
20837      */
20838     applyConfig: function() {
20839         Roo.dd.DD.superclass.applyConfig.call(this);
20840         this.scroll = (this.config.scroll !== false);
20841     },
20842
20843     /*
20844      * Event that fires prior to the onMouseDown event.  Overrides
20845      * Roo.dd.DragDrop.
20846      */
20847     b4MouseDown: function(e) {
20848         // this.resetConstraints();
20849         this.autoOffset(e.getPageX(),
20850                             e.getPageY());
20851     },
20852
20853     /*
20854      * Event that fires prior to the onDrag event.  Overrides
20855      * Roo.dd.DragDrop.
20856      */
20857     b4Drag: function(e) {
20858         this.setDragElPos(e.getPageX(),
20859                             e.getPageY());
20860     },
20861
20862     toString: function() {
20863         return ("DD " + this.id);
20864     }
20865
20866     //////////////////////////////////////////////////////////////////////////
20867     // Debugging ygDragDrop events that can be overridden
20868     //////////////////////////////////////////////////////////////////////////
20869     /*
20870     startDrag: function(x, y) {
20871     },
20872
20873     onDrag: function(e) {
20874     },
20875
20876     onDragEnter: function(e, id) {
20877     },
20878
20879     onDragOver: function(e, id) {
20880     },
20881
20882     onDragOut: function(e, id) {
20883     },
20884
20885     onDragDrop: function(e, id) {
20886     },
20887
20888     endDrag: function(e) {
20889     }
20890
20891     */
20892
20893 });/*
20894  * Based on:
20895  * Ext JS Library 1.1.1
20896  * Copyright(c) 2006-2007, Ext JS, LLC.
20897  *
20898  * Originally Released Under LGPL - original licence link has changed is not relivant.
20899  *
20900  * Fork - LGPL
20901  * <script type="text/javascript">
20902  */
20903
20904 /**
20905  * @class Roo.dd.DDProxy
20906  * A DragDrop implementation that inserts an empty, bordered div into
20907  * the document that follows the cursor during drag operations.  At the time of
20908  * the click, the frame div is resized to the dimensions of the linked html
20909  * element, and moved to the exact location of the linked element.
20910  *
20911  * References to the "frame" element refer to the single proxy element that
20912  * was created to be dragged in place of all DDProxy elements on the
20913  * page.
20914  *
20915  * @extends Roo.dd.DD
20916  * @constructor
20917  * @param {String} id the id of the linked html element
20918  * @param {String} sGroup the group of related DragDrop objects
20919  * @param {object} config an object containing configurable attributes
20920  *                Valid properties for DDProxy in addition to those in DragDrop:
20921  *                   resizeFrame, centerFrame, dragElId
20922  */
20923 Roo.dd.DDProxy = function(id, sGroup, config) {
20924     if (id) {
20925         this.init(id, sGroup, config);
20926         this.initFrame();
20927     }
20928 };
20929
20930 /**
20931  * The default drag frame div id
20932  * @property Roo.dd.DDProxy.dragElId
20933  * @type String
20934  * @static
20935  */
20936 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20937
20938 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20939
20940     /**
20941      * By default we resize the drag frame to be the same size as the element
20942      * we want to drag (this is to get the frame effect).  We can turn it off
20943      * if we want a different behavior.
20944      * @property resizeFrame
20945      * @type boolean
20946      */
20947     resizeFrame: true,
20948
20949     /**
20950      * By default the frame is positioned exactly where the drag element is, so
20951      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20952      * you do not have constraints on the obj is to have the drag frame centered
20953      * around the cursor.  Set centerFrame to true for this effect.
20954      * @property centerFrame
20955      * @type boolean
20956      */
20957     centerFrame: false,
20958
20959     /**
20960      * Creates the proxy element if it does not yet exist
20961      * @method createFrame
20962      */
20963     createFrame: function() {
20964         var self = this;
20965         var body = document.body;
20966
20967         if (!body || !body.firstChild) {
20968             setTimeout( function() { self.createFrame(); }, 50 );
20969             return;
20970         }
20971
20972         var div = this.getDragEl();
20973
20974         if (!div) {
20975             div    = document.createElement("div");
20976             div.id = this.dragElId;
20977             var s  = div.style;
20978
20979             s.position   = "absolute";
20980             s.visibility = "hidden";
20981             s.cursor     = "move";
20982             s.border     = "2px solid #aaa";
20983             s.zIndex     = 999;
20984
20985             // appendChild can blow up IE if invoked prior to the window load event
20986             // while rendering a table.  It is possible there are other scenarios
20987             // that would cause this to happen as well.
20988             body.insertBefore(div, body.firstChild);
20989         }
20990     },
20991
20992     /**
20993      * Initialization for the drag frame element.  Must be called in the
20994      * constructor of all subclasses
20995      * @method initFrame
20996      */
20997     initFrame: function() {
20998         this.createFrame();
20999     },
21000
21001     applyConfig: function() {
21002         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21003
21004         this.resizeFrame = (this.config.resizeFrame !== false);
21005         this.centerFrame = (this.config.centerFrame);
21006         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21007     },
21008
21009     /**
21010      * Resizes the drag frame to the dimensions of the clicked object, positions
21011      * it over the object, and finally displays it
21012      * @method showFrame
21013      * @param {int} iPageX X click position
21014      * @param {int} iPageY Y click position
21015      * @private
21016      */
21017     showFrame: function(iPageX, iPageY) {
21018         var el = this.getEl();
21019         var dragEl = this.getDragEl();
21020         var s = dragEl.style;
21021
21022         this._resizeProxy();
21023
21024         if (this.centerFrame) {
21025             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21026                            Math.round(parseInt(s.height, 10)/2) );
21027         }
21028
21029         this.setDragElPos(iPageX, iPageY);
21030
21031         Roo.fly(dragEl).show();
21032     },
21033
21034     /**
21035      * The proxy is automatically resized to the dimensions of the linked
21036      * element when a drag is initiated, unless resizeFrame is set to false
21037      * @method _resizeProxy
21038      * @private
21039      */
21040     _resizeProxy: function() {
21041         if (this.resizeFrame) {
21042             var el = this.getEl();
21043             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21044         }
21045     },
21046
21047     // overrides Roo.dd.DragDrop
21048     b4MouseDown: function(e) {
21049         var x = e.getPageX();
21050         var y = e.getPageY();
21051         this.autoOffset(x, y);
21052         this.setDragElPos(x, y);
21053     },
21054
21055     // overrides Roo.dd.DragDrop
21056     b4StartDrag: function(x, y) {
21057         // show the drag frame
21058         this.showFrame(x, y);
21059     },
21060
21061     // overrides Roo.dd.DragDrop
21062     b4EndDrag: function(e) {
21063         Roo.fly(this.getDragEl()).hide();
21064     },
21065
21066     // overrides Roo.dd.DragDrop
21067     // By default we try to move the element to the last location of the frame.
21068     // This is so that the default behavior mirrors that of Roo.dd.DD.
21069     endDrag: function(e) {
21070
21071         var lel = this.getEl();
21072         var del = this.getDragEl();
21073
21074         // Show the drag frame briefly so we can get its position
21075         del.style.visibility = "";
21076
21077         this.beforeMove();
21078         // Hide the linked element before the move to get around a Safari
21079         // rendering bug.
21080         lel.style.visibility = "hidden";
21081         Roo.dd.DDM.moveToEl(lel, del);
21082         del.style.visibility = "hidden";
21083         lel.style.visibility = "";
21084
21085         this.afterDrag();
21086     },
21087
21088     beforeMove : function(){
21089
21090     },
21091
21092     afterDrag : function(){
21093
21094     },
21095
21096     toString: function() {
21097         return ("DDProxy " + this.id);
21098     }
21099
21100 });
21101 /*
21102  * Based on:
21103  * Ext JS Library 1.1.1
21104  * Copyright(c) 2006-2007, Ext JS, LLC.
21105  *
21106  * Originally Released Under LGPL - original licence link has changed is not relivant.
21107  *
21108  * Fork - LGPL
21109  * <script type="text/javascript">
21110  */
21111
21112  /**
21113  * @class Roo.dd.DDTarget
21114  * A DragDrop implementation that does not move, but can be a drop
21115  * target.  You would get the same result by simply omitting implementation
21116  * for the event callbacks, but this way we reduce the processing cost of the
21117  * event listener and the callbacks.
21118  * @extends Roo.dd.DragDrop
21119  * @constructor
21120  * @param {String} id the id of the element that is a drop target
21121  * @param {String} sGroup the group of related DragDrop objects
21122  * @param {object} config an object containing configurable attributes
21123  *                 Valid properties for DDTarget in addition to those in
21124  *                 DragDrop:
21125  *                    none
21126  */
21127 Roo.dd.DDTarget = function(id, sGroup, config) {
21128     if (id) {
21129         this.initTarget(id, sGroup, config);
21130     }
21131     if (config && (config.listeners || config.events)) { 
21132         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21133             listeners : config.listeners || {}, 
21134             events : config.events || {} 
21135         });    
21136     }
21137 };
21138
21139 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21140 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21141     toString: function() {
21142         return ("DDTarget " + this.id);
21143     }
21144 });
21145 /*
21146  * Based on:
21147  * Ext JS Library 1.1.1
21148  * Copyright(c) 2006-2007, Ext JS, LLC.
21149  *
21150  * Originally Released Under LGPL - original licence link has changed is not relivant.
21151  *
21152  * Fork - LGPL
21153  * <script type="text/javascript">
21154  */
21155  
21156
21157 /**
21158  * @class Roo.dd.ScrollManager
21159  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21160  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21161  * @singleton
21162  */
21163 Roo.dd.ScrollManager = function(){
21164     var ddm = Roo.dd.DragDropMgr;
21165     var els = {};
21166     var dragEl = null;
21167     var proc = {};
21168     
21169     
21170     
21171     var onStop = function(e){
21172         dragEl = null;
21173         clearProc();
21174     };
21175     
21176     var triggerRefresh = function(){
21177         if(ddm.dragCurrent){
21178              ddm.refreshCache(ddm.dragCurrent.groups);
21179         }
21180     };
21181     
21182     var doScroll = function(){
21183         if(ddm.dragCurrent){
21184             var dds = Roo.dd.ScrollManager;
21185             if(!dds.animate){
21186                 if(proc.el.scroll(proc.dir, dds.increment)){
21187                     triggerRefresh();
21188                 }
21189             }else{
21190                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21191             }
21192         }
21193     };
21194     
21195     var clearProc = function(){
21196         if(proc.id){
21197             clearInterval(proc.id);
21198         }
21199         proc.id = 0;
21200         proc.el = null;
21201         proc.dir = "";
21202     };
21203     
21204     var startProc = function(el, dir){
21205          Roo.log('scroll startproc');
21206         clearProc();
21207         proc.el = el;
21208         proc.dir = dir;
21209         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21210     };
21211     
21212     var onFire = function(e, isDrop){
21213        
21214         if(isDrop || !ddm.dragCurrent){ return; }
21215         var dds = Roo.dd.ScrollManager;
21216         if(!dragEl || dragEl != ddm.dragCurrent){
21217             dragEl = ddm.dragCurrent;
21218             // refresh regions on drag start
21219             dds.refreshCache();
21220         }
21221         
21222         var xy = Roo.lib.Event.getXY(e);
21223         var pt = new Roo.lib.Point(xy[0], xy[1]);
21224         for(var id in els){
21225             var el = els[id], r = el._region;
21226             if(r && r.contains(pt) && el.isScrollable()){
21227                 if(r.bottom - pt.y <= dds.thresh){
21228                     if(proc.el != el){
21229                         startProc(el, "down");
21230                     }
21231                     return;
21232                 }else if(r.right - pt.x <= dds.thresh){
21233                     if(proc.el != el){
21234                         startProc(el, "left");
21235                     }
21236                     return;
21237                 }else if(pt.y - r.top <= dds.thresh){
21238                     if(proc.el != el){
21239                         startProc(el, "up");
21240                     }
21241                     return;
21242                 }else if(pt.x - r.left <= dds.thresh){
21243                     if(proc.el != el){
21244                         startProc(el, "right");
21245                     }
21246                     return;
21247                 }
21248             }
21249         }
21250         clearProc();
21251     };
21252     
21253     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21254     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21255     
21256     return {
21257         /**
21258          * Registers new overflow element(s) to auto scroll
21259          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21260          */
21261         register : function(el){
21262             if(el instanceof Array){
21263                 for(var i = 0, len = el.length; i < len; i++) {
21264                         this.register(el[i]);
21265                 }
21266             }else{
21267                 el = Roo.get(el);
21268                 els[el.id] = el;
21269             }
21270             Roo.dd.ScrollManager.els = els;
21271         },
21272         
21273         /**
21274          * Unregisters overflow element(s) so they are no longer scrolled
21275          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21276          */
21277         unregister : function(el){
21278             if(el instanceof Array){
21279                 for(var i = 0, len = el.length; i < len; i++) {
21280                         this.unregister(el[i]);
21281                 }
21282             }else{
21283                 el = Roo.get(el);
21284                 delete els[el.id];
21285             }
21286         },
21287         
21288         /**
21289          * The number of pixels from the edge of a container the pointer needs to be to 
21290          * trigger scrolling (defaults to 25)
21291          * @type Number
21292          */
21293         thresh : 25,
21294         
21295         /**
21296          * The number of pixels to scroll in each scroll increment (defaults to 50)
21297          * @type Number
21298          */
21299         increment : 100,
21300         
21301         /**
21302          * The frequency of scrolls in milliseconds (defaults to 500)
21303          * @type Number
21304          */
21305         frequency : 500,
21306         
21307         /**
21308          * True to animate the scroll (defaults to true)
21309          * @type Boolean
21310          */
21311         animate: true,
21312         
21313         /**
21314          * The animation duration in seconds - 
21315          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21316          * @type Number
21317          */
21318         animDuration: .4,
21319         
21320         /**
21321          * Manually trigger a cache refresh.
21322          */
21323         refreshCache : function(){
21324             for(var id in els){
21325                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21326                     els[id]._region = els[id].getRegion();
21327                 }
21328             }
21329         }
21330     };
21331 }();/*
21332  * Based on:
21333  * Ext JS Library 1.1.1
21334  * Copyright(c) 2006-2007, Ext JS, LLC.
21335  *
21336  * Originally Released Under LGPL - original licence link has changed is not relivant.
21337  *
21338  * Fork - LGPL
21339  * <script type="text/javascript">
21340  */
21341  
21342
21343 /**
21344  * @class Roo.dd.Registry
21345  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21346  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21347  * @singleton
21348  */
21349 Roo.dd.Registry = function(){
21350     var elements = {}; 
21351     var handles = {}; 
21352     var autoIdSeed = 0;
21353
21354     var getId = function(el, autogen){
21355         if(typeof el == "string"){
21356             return el;
21357         }
21358         var id = el.id;
21359         if(!id && autogen !== false){
21360             id = "roodd-" + (++autoIdSeed);
21361             el.id = id;
21362         }
21363         return id;
21364     };
21365     
21366     return {
21367     /**
21368      * Register a drag drop element
21369      * @param {String|HTMLElement} element The id or DOM node to register
21370      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21371      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21372      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21373      * populated in the data object (if applicable):
21374      * <pre>
21375 Value      Description<br />
21376 ---------  ------------------------------------------<br />
21377 handles    Array of DOM nodes that trigger dragging<br />
21378            for the element being registered<br />
21379 isHandle   True if the element passed in triggers<br />
21380            dragging itself, else false
21381 </pre>
21382      */
21383         register : function(el, data){
21384             data = data || {};
21385             if(typeof el == "string"){
21386                 el = document.getElementById(el);
21387             }
21388             data.ddel = el;
21389             elements[getId(el)] = data;
21390             if(data.isHandle !== false){
21391                 handles[data.ddel.id] = data;
21392             }
21393             if(data.handles){
21394                 var hs = data.handles;
21395                 for(var i = 0, len = hs.length; i < len; i++){
21396                         handles[getId(hs[i])] = data;
21397                 }
21398             }
21399         },
21400
21401     /**
21402      * Unregister a drag drop element
21403      * @param {String|HTMLElement}  element The id or DOM node to unregister
21404      */
21405         unregister : function(el){
21406             var id = getId(el, false);
21407             var data = elements[id];
21408             if(data){
21409                 delete elements[id];
21410                 if(data.handles){
21411                     var hs = data.handles;
21412                     for(var i = 0, len = hs.length; i < len; i++){
21413                         delete handles[getId(hs[i], false)];
21414                     }
21415                 }
21416             }
21417         },
21418
21419     /**
21420      * Returns the handle registered for a DOM Node by id
21421      * @param {String|HTMLElement} id The DOM node or id to look up
21422      * @return {Object} handle The custom handle data
21423      */
21424         getHandle : function(id){
21425             if(typeof id != "string"){ // must be element?
21426                 id = id.id;
21427             }
21428             return handles[id];
21429         },
21430
21431     /**
21432      * Returns the handle that is registered for the DOM node that is the target of the event
21433      * @param {Event} e The event
21434      * @return {Object} handle The custom handle data
21435      */
21436         getHandleFromEvent : function(e){
21437             var t = Roo.lib.Event.getTarget(e);
21438             return t ? handles[t.id] : null;
21439         },
21440
21441     /**
21442      * Returns a custom data object that is registered for a DOM node by id
21443      * @param {String|HTMLElement} id The DOM node or id to look up
21444      * @return {Object} data The custom data
21445      */
21446         getTarget : function(id){
21447             if(typeof id != "string"){ // must be element?
21448                 id = id.id;
21449             }
21450             return elements[id];
21451         },
21452
21453     /**
21454      * Returns a custom data object that is registered for the DOM node that is the target of the event
21455      * @param {Event} e The event
21456      * @return {Object} data The custom data
21457      */
21458         getTargetFromEvent : function(e){
21459             var t = Roo.lib.Event.getTarget(e);
21460             return t ? elements[t.id] || handles[t.id] : null;
21461         }
21462     };
21463 }();/*
21464  * Based on:
21465  * Ext JS Library 1.1.1
21466  * Copyright(c) 2006-2007, Ext JS, LLC.
21467  *
21468  * Originally Released Under LGPL - original licence link has changed is not relivant.
21469  *
21470  * Fork - LGPL
21471  * <script type="text/javascript">
21472  */
21473  
21474
21475 /**
21476  * @class Roo.dd.StatusProxy
21477  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21478  * default drag proxy used by all Roo.dd components.
21479  * @constructor
21480  * @param {Object} config
21481  */
21482 Roo.dd.StatusProxy = function(config){
21483     Roo.apply(this, config);
21484     this.id = this.id || Roo.id();
21485     this.el = new Roo.Layer({
21486         dh: {
21487             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21488                 {tag: "div", cls: "x-dd-drop-icon"},
21489                 {tag: "div", cls: "x-dd-drag-ghost"}
21490             ]
21491         }, 
21492         shadow: !config || config.shadow !== false
21493     });
21494     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21495     this.dropStatus = this.dropNotAllowed;
21496 };
21497
21498 Roo.dd.StatusProxy.prototype = {
21499     /**
21500      * @cfg {String} dropAllowed
21501      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21502      */
21503     dropAllowed : "x-dd-drop-ok",
21504     /**
21505      * @cfg {String} dropNotAllowed
21506      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21507      */
21508     dropNotAllowed : "x-dd-drop-nodrop",
21509
21510     /**
21511      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21512      * over the current target element.
21513      * @param {String} cssClass The css class for the new drop status indicator image
21514      */
21515     setStatus : function(cssClass){
21516         cssClass = cssClass || this.dropNotAllowed;
21517         if(this.dropStatus != cssClass){
21518             this.el.replaceClass(this.dropStatus, cssClass);
21519             this.dropStatus = cssClass;
21520         }
21521     },
21522
21523     /**
21524      * Resets the status indicator to the default dropNotAllowed value
21525      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21526      */
21527     reset : function(clearGhost){
21528         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21529         this.dropStatus = this.dropNotAllowed;
21530         if(clearGhost){
21531             this.ghost.update("");
21532         }
21533     },
21534
21535     /**
21536      * Updates the contents of the ghost element
21537      * @param {String} html The html that will replace the current innerHTML of the ghost element
21538      */
21539     update : function(html){
21540         if(typeof html == "string"){
21541             this.ghost.update(html);
21542         }else{
21543             this.ghost.update("");
21544             html.style.margin = "0";
21545             this.ghost.dom.appendChild(html);
21546         }
21547         // ensure float = none set?? cant remember why though.
21548         var el = this.ghost.dom.firstChild;
21549                 if(el){
21550                         Roo.fly(el).setStyle('float', 'none');
21551                 }
21552     },
21553     
21554     /**
21555      * Returns the underlying proxy {@link Roo.Layer}
21556      * @return {Roo.Layer} el
21557     */
21558     getEl : function(){
21559         return this.el;
21560     },
21561
21562     /**
21563      * Returns the ghost element
21564      * @return {Roo.Element} el
21565      */
21566     getGhost : function(){
21567         return this.ghost;
21568     },
21569
21570     /**
21571      * Hides the proxy
21572      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21573      */
21574     hide : function(clear){
21575         this.el.hide();
21576         if(clear){
21577             this.reset(true);
21578         }
21579     },
21580
21581     /**
21582      * Stops the repair animation if it's currently running
21583      */
21584     stop : function(){
21585         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21586             this.anim.stop();
21587         }
21588     },
21589
21590     /**
21591      * Displays this proxy
21592      */
21593     show : function(){
21594         this.el.show();
21595     },
21596
21597     /**
21598      * Force the Layer to sync its shadow and shim positions to the element
21599      */
21600     sync : function(){
21601         this.el.sync();
21602     },
21603
21604     /**
21605      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21606      * invalid drop operation by the item being dragged.
21607      * @param {Array} xy The XY position of the element ([x, y])
21608      * @param {Function} callback The function to call after the repair is complete
21609      * @param {Object} scope The scope in which to execute the callback
21610      */
21611     repair : function(xy, callback, scope){
21612         this.callback = callback;
21613         this.scope = scope;
21614         if(xy && this.animRepair !== false){
21615             this.el.addClass("x-dd-drag-repair");
21616             this.el.hideUnders(true);
21617             this.anim = this.el.shift({
21618                 duration: this.repairDuration || .5,
21619                 easing: 'easeOut',
21620                 xy: xy,
21621                 stopFx: true,
21622                 callback: this.afterRepair,
21623                 scope: this
21624             });
21625         }else{
21626             this.afterRepair();
21627         }
21628     },
21629
21630     // private
21631     afterRepair : function(){
21632         this.hide(true);
21633         if(typeof this.callback == "function"){
21634             this.callback.call(this.scope || this);
21635         }
21636         this.callback = null;
21637         this.scope = null;
21638     }
21639 };/*
21640  * Based on:
21641  * Ext JS Library 1.1.1
21642  * Copyright(c) 2006-2007, Ext JS, LLC.
21643  *
21644  * Originally Released Under LGPL - original licence link has changed is not relivant.
21645  *
21646  * Fork - LGPL
21647  * <script type="text/javascript">
21648  */
21649
21650 /**
21651  * @class Roo.dd.DragSource
21652  * @extends Roo.dd.DDProxy
21653  * A simple class that provides the basic implementation needed to make any element draggable.
21654  * @constructor
21655  * @param {String/HTMLElement/Element} el The container element
21656  * @param {Object} config
21657  */
21658 Roo.dd.DragSource = function(el, config){
21659     this.el = Roo.get(el);
21660     this.dragData = {};
21661     
21662     Roo.apply(this, config);
21663     
21664     if(!this.proxy){
21665         this.proxy = new Roo.dd.StatusProxy();
21666     }
21667
21668     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21669           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21670     
21671     this.dragging = false;
21672 };
21673
21674 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21675     /**
21676      * @cfg {String} dropAllowed
21677      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21678      */
21679     dropAllowed : "x-dd-drop-ok",
21680     /**
21681      * @cfg {String} dropNotAllowed
21682      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21683      */
21684     dropNotAllowed : "x-dd-drop-nodrop",
21685
21686     /**
21687      * Returns the data object associated with this drag source
21688      * @return {Object} data An object containing arbitrary data
21689      */
21690     getDragData : function(e){
21691         return this.dragData;
21692     },
21693
21694     // private
21695     onDragEnter : function(e, id){
21696         var target = Roo.dd.DragDropMgr.getDDById(id);
21697         this.cachedTarget = target;
21698         if(this.beforeDragEnter(target, e, id) !== false){
21699             if(target.isNotifyTarget){
21700                 var status = target.notifyEnter(this, e, this.dragData);
21701                 this.proxy.setStatus(status);
21702             }else{
21703                 this.proxy.setStatus(this.dropAllowed);
21704             }
21705             
21706             if(this.afterDragEnter){
21707                 /**
21708                  * An empty function by default, but provided so that you can perform a custom action
21709                  * when the dragged item enters the drop target by providing an implementation.
21710                  * @param {Roo.dd.DragDrop} target The drop target
21711                  * @param {Event} e The event object
21712                  * @param {String} id The id of the dragged element
21713                  * @method afterDragEnter
21714                  */
21715                 this.afterDragEnter(target, e, id);
21716             }
21717         }
21718     },
21719
21720     /**
21721      * An empty function by default, but provided so that you can perform a custom action
21722      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21723      * @param {Roo.dd.DragDrop} target The drop target
21724      * @param {Event} e The event object
21725      * @param {String} id The id of the dragged element
21726      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21727      */
21728     beforeDragEnter : function(target, e, id){
21729         return true;
21730     },
21731
21732     // private
21733     alignElWithMouse: function() {
21734         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21735         this.proxy.sync();
21736     },
21737
21738     // private
21739     onDragOver : function(e, id){
21740         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21741         if(this.beforeDragOver(target, e, id) !== false){
21742             if(target.isNotifyTarget){
21743                 var status = target.notifyOver(this, e, this.dragData);
21744                 this.proxy.setStatus(status);
21745             }
21746
21747             if(this.afterDragOver){
21748                 /**
21749                  * An empty function by default, but provided so that you can perform a custom action
21750                  * while the dragged item is over the drop target by providing an implementation.
21751                  * @param {Roo.dd.DragDrop} target The drop target
21752                  * @param {Event} e The event object
21753                  * @param {String} id The id of the dragged element
21754                  * @method afterDragOver
21755                  */
21756                 this.afterDragOver(target, e, id);
21757             }
21758         }
21759     },
21760
21761     /**
21762      * An empty function by default, but provided so that you can perform a custom action
21763      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21764      * @param {Roo.dd.DragDrop} target The drop target
21765      * @param {Event} e The event object
21766      * @param {String} id The id of the dragged element
21767      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21768      */
21769     beforeDragOver : function(target, e, id){
21770         return true;
21771     },
21772
21773     // private
21774     onDragOut : function(e, id){
21775         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21776         if(this.beforeDragOut(target, e, id) !== false){
21777             if(target.isNotifyTarget){
21778                 target.notifyOut(this, e, this.dragData);
21779             }
21780             this.proxy.reset();
21781             if(this.afterDragOut){
21782                 /**
21783                  * An empty function by default, but provided so that you can perform a custom action
21784                  * after the dragged item is dragged out of the target without dropping.
21785                  * @param {Roo.dd.DragDrop} target The drop target
21786                  * @param {Event} e The event object
21787                  * @param {String} id The id of the dragged element
21788                  * @method afterDragOut
21789                  */
21790                 this.afterDragOut(target, e, id);
21791             }
21792         }
21793         this.cachedTarget = null;
21794     },
21795
21796     /**
21797      * An empty function by default, but provided so that you can perform a custom action before the dragged
21798      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21799      * @param {Roo.dd.DragDrop} target The drop target
21800      * @param {Event} e The event object
21801      * @param {String} id The id of the dragged element
21802      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21803      */
21804     beforeDragOut : function(target, e, id){
21805         return true;
21806     },
21807     
21808     // private
21809     onDragDrop : function(e, id){
21810         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21811         if(this.beforeDragDrop(target, e, id) !== false){
21812             if(target.isNotifyTarget){
21813                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21814                     this.onValidDrop(target, e, id);
21815                 }else{
21816                     this.onInvalidDrop(target, e, id);
21817                 }
21818             }else{
21819                 this.onValidDrop(target, e, id);
21820             }
21821             
21822             if(this.afterDragDrop){
21823                 /**
21824                  * An empty function by default, but provided so that you can perform a custom action
21825                  * after a valid drag drop has occurred by providing an implementation.
21826                  * @param {Roo.dd.DragDrop} target The drop target
21827                  * @param {Event} e The event object
21828                  * @param {String} id The id of the dropped element
21829                  * @method afterDragDrop
21830                  */
21831                 this.afterDragDrop(target, e, id);
21832             }
21833         }
21834         delete this.cachedTarget;
21835     },
21836
21837     /**
21838      * An empty function by default, but provided so that you can perform a custom action before the dragged
21839      * item is dropped onto the target and optionally cancel the onDragDrop.
21840      * @param {Roo.dd.DragDrop} target The drop target
21841      * @param {Event} e The event object
21842      * @param {String} id The id of the dragged element
21843      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21844      */
21845     beforeDragDrop : function(target, e, id){
21846         return true;
21847     },
21848
21849     // private
21850     onValidDrop : function(target, e, id){
21851         this.hideProxy();
21852         if(this.afterValidDrop){
21853             /**
21854              * An empty function by default, but provided so that you can perform a custom action
21855              * after a valid drop has occurred by providing an implementation.
21856              * @param {Object} target The target DD 
21857              * @param {Event} e The event object
21858              * @param {String} id The id of the dropped element
21859              * @method afterInvalidDrop
21860              */
21861             this.afterValidDrop(target, e, id);
21862         }
21863     },
21864
21865     // private
21866     getRepairXY : function(e, data){
21867         return this.el.getXY();  
21868     },
21869
21870     // private
21871     onInvalidDrop : function(target, e, id){
21872         this.beforeInvalidDrop(target, e, id);
21873         if(this.cachedTarget){
21874             if(this.cachedTarget.isNotifyTarget){
21875                 this.cachedTarget.notifyOut(this, e, this.dragData);
21876             }
21877             this.cacheTarget = null;
21878         }
21879         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21880
21881         if(this.afterInvalidDrop){
21882             /**
21883              * An empty function by default, but provided so that you can perform a custom action
21884              * after an invalid drop has occurred by providing an implementation.
21885              * @param {Event} e The event object
21886              * @param {String} id The id of the dropped element
21887              * @method afterInvalidDrop
21888              */
21889             this.afterInvalidDrop(e, id);
21890         }
21891     },
21892
21893     // private
21894     afterRepair : function(){
21895         if(Roo.enableFx){
21896             this.el.highlight(this.hlColor || "c3daf9");
21897         }
21898         this.dragging = false;
21899     },
21900
21901     /**
21902      * An empty function by default, but provided so that you can perform a custom action after an invalid
21903      * drop has occurred.
21904      * @param {Roo.dd.DragDrop} target The drop target
21905      * @param {Event} e The event object
21906      * @param {String} id The id of the dragged element
21907      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21908      */
21909     beforeInvalidDrop : function(target, e, id){
21910         return true;
21911     },
21912
21913     // private
21914     handleMouseDown : function(e){
21915         if(this.dragging) {
21916             return;
21917         }
21918         var data = this.getDragData(e);
21919         if(data && this.onBeforeDrag(data, e) !== false){
21920             this.dragData = data;
21921             this.proxy.stop();
21922             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21923         } 
21924     },
21925
21926     /**
21927      * An empty function by default, but provided so that you can perform a custom action before the initial
21928      * drag event begins and optionally cancel it.
21929      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21930      * @param {Event} e The event object
21931      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21932      */
21933     onBeforeDrag : function(data, e){
21934         return true;
21935     },
21936
21937     /**
21938      * An empty function by default, but provided so that you can perform a custom action once the initial
21939      * drag event has begun.  The drag cannot be canceled from this function.
21940      * @param {Number} x The x position of the click on the dragged object
21941      * @param {Number} y The y position of the click on the dragged object
21942      */
21943     onStartDrag : Roo.emptyFn,
21944
21945     // private - YUI override
21946     startDrag : function(x, y){
21947         this.proxy.reset();
21948         this.dragging = true;
21949         this.proxy.update("");
21950         this.onInitDrag(x, y);
21951         this.proxy.show();
21952     },
21953
21954     // private
21955     onInitDrag : function(x, y){
21956         var clone = this.el.dom.cloneNode(true);
21957         clone.id = Roo.id(); // prevent duplicate ids
21958         this.proxy.update(clone);
21959         this.onStartDrag(x, y);
21960         return true;
21961     },
21962
21963     /**
21964      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21965      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21966      */
21967     getProxy : function(){
21968         return this.proxy;  
21969     },
21970
21971     /**
21972      * Hides the drag source's {@link Roo.dd.StatusProxy}
21973      */
21974     hideProxy : function(){
21975         this.proxy.hide();  
21976         this.proxy.reset(true);
21977         this.dragging = false;
21978     },
21979
21980     // private
21981     triggerCacheRefresh : function(){
21982         Roo.dd.DDM.refreshCache(this.groups);
21983     },
21984
21985     // private - override to prevent hiding
21986     b4EndDrag: function(e) {
21987     },
21988
21989     // private - override to prevent moving
21990     endDrag : function(e){
21991         this.onEndDrag(this.dragData, e);
21992     },
21993
21994     // private
21995     onEndDrag : function(data, e){
21996     },
21997     
21998     // private - pin to cursor
21999     autoOffset : function(x, y) {
22000         this.setDelta(-12, -20);
22001     }    
22002 });/*
22003  * Based on:
22004  * Ext JS Library 1.1.1
22005  * Copyright(c) 2006-2007, Ext JS, LLC.
22006  *
22007  * Originally Released Under LGPL - original licence link has changed is not relivant.
22008  *
22009  * Fork - LGPL
22010  * <script type="text/javascript">
22011  */
22012
22013
22014 /**
22015  * @class Roo.dd.DropTarget
22016  * @extends Roo.dd.DDTarget
22017  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22018  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22019  * @constructor
22020  * @param {String/HTMLElement/Element} el The container element
22021  * @param {Object} config
22022  */
22023 Roo.dd.DropTarget = function(el, config){
22024     this.el = Roo.get(el);
22025     
22026     var listeners = false; ;
22027     if (config && config.listeners) {
22028         listeners= config.listeners;
22029         delete config.listeners;
22030     }
22031     Roo.apply(this, config);
22032     
22033     if(this.containerScroll){
22034         Roo.dd.ScrollManager.register(this.el);
22035     }
22036     this.addEvents( {
22037          /**
22038          * @scope Roo.dd.DropTarget
22039          */
22040          
22041          /**
22042          * @event enter
22043          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22044          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22045          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22046          * 
22047          * IMPORTANT : it should set this.overClass and this.dropAllowed
22048          * 
22049          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22050          * @param {Event} e The event
22051          * @param {Object} data An object containing arbitrary data supplied by the drag source
22052          */
22053         "enter" : true,
22054         
22055          /**
22056          * @event over
22057          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22058          * This method will be called on every mouse movement while the drag source is over the drop target.
22059          * This default implementation simply returns the dropAllowed config value.
22060          * 
22061          * IMPORTANT : it should set this.dropAllowed
22062          * 
22063          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22064          * @param {Event} e The event
22065          * @param {Object} data An object containing arbitrary data supplied by the drag source
22066          
22067          */
22068         "over" : true,
22069         /**
22070          * @event out
22071          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22072          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22073          * overClass (if any) from the drop element.
22074          * 
22075          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22076          * @param {Event} e The event
22077          * @param {Object} data An object containing arbitrary data supplied by the drag source
22078          */
22079          "out" : true,
22080          
22081         /**
22082          * @event drop
22083          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22084          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22085          * implementation that does something to process the drop event and returns true so that the drag source's
22086          * repair action does not run.
22087          * 
22088          * IMPORTANT : it should set this.success
22089          * 
22090          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22091          * @param {Event} e The event
22092          * @param {Object} data An object containing arbitrary data supplied by the drag source
22093         */
22094          "drop" : true
22095     });
22096             
22097      
22098     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22099         this.el.dom, 
22100         this.ddGroup || this.group,
22101         {
22102             isTarget: true,
22103             listeners : listeners || {} 
22104            
22105         
22106         }
22107     );
22108
22109 };
22110
22111 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22112     /**
22113      * @cfg {String} overClass
22114      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22115      */
22116      /**
22117      * @cfg {String} ddGroup
22118      * The drag drop group to handle drop events for
22119      */
22120      
22121     /**
22122      * @cfg {String} dropAllowed
22123      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22124      */
22125     dropAllowed : "x-dd-drop-ok",
22126     /**
22127      * @cfg {String} dropNotAllowed
22128      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22129      */
22130     dropNotAllowed : "x-dd-drop-nodrop",
22131     /**
22132      * @cfg {boolean} success
22133      * set this after drop listener.. 
22134      */
22135     success : false,
22136     /**
22137      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22138      * if the drop point is valid for over/enter..
22139      */
22140     valid : false,
22141     // private
22142     isTarget : true,
22143
22144     // private
22145     isNotifyTarget : true,
22146     
22147     /**
22148      * @hide
22149      */
22150     notifyEnter : function(dd, e, data)
22151     {
22152         this.valid = true;
22153         this.fireEvent('enter', dd, e, data);
22154         if(this.overClass){
22155             this.el.addClass(this.overClass);
22156         }
22157         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22158             this.valid ? this.dropAllowed : this.dropNotAllowed
22159         );
22160     },
22161
22162     /**
22163      * @hide
22164      */
22165     notifyOver : function(dd, e, data)
22166     {
22167         this.valid = true;
22168         this.fireEvent('over', dd, e, data);
22169         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22170             this.valid ? this.dropAllowed : this.dropNotAllowed
22171         );
22172     },
22173
22174     /**
22175      * @hide
22176      */
22177     notifyOut : function(dd, e, data)
22178     {
22179         this.fireEvent('out', dd, e, data);
22180         if(this.overClass){
22181             this.el.removeClass(this.overClass);
22182         }
22183     },
22184
22185     /**
22186      * @hide
22187      */
22188     notifyDrop : function(dd, e, data)
22189     {
22190         this.success = false;
22191         this.fireEvent('drop', dd, e, data);
22192         return this.success;
22193     }
22194 });/*
22195  * Based on:
22196  * Ext JS Library 1.1.1
22197  * Copyright(c) 2006-2007, Ext JS, LLC.
22198  *
22199  * Originally Released Under LGPL - original licence link has changed is not relivant.
22200  *
22201  * Fork - LGPL
22202  * <script type="text/javascript">
22203  */
22204
22205
22206 /**
22207  * @class Roo.dd.DragZone
22208  * @extends Roo.dd.DragSource
22209  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22210  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22211  * @constructor
22212  * @param {String/HTMLElement/Element} el The container element
22213  * @param {Object} config
22214  */
22215 Roo.dd.DragZone = function(el, config){
22216     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22217     if(this.containerScroll){
22218         Roo.dd.ScrollManager.register(this.el);
22219     }
22220 };
22221
22222 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22223     /**
22224      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22225      * for auto scrolling during drag operations.
22226      */
22227     /**
22228      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22229      * method after a failed drop (defaults to "c3daf9" - light blue)
22230      */
22231
22232     /**
22233      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22234      * for a valid target to drag based on the mouse down. Override this method
22235      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22236      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22237      * @param {EventObject} e The mouse down event
22238      * @return {Object} The dragData
22239      */
22240     getDragData : function(e){
22241         return Roo.dd.Registry.getHandleFromEvent(e);
22242     },
22243     
22244     /**
22245      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22246      * this.dragData.ddel
22247      * @param {Number} x The x position of the click on the dragged object
22248      * @param {Number} y The y position of the click on the dragged object
22249      * @return {Boolean} true to continue the drag, false to cancel
22250      */
22251     onInitDrag : function(x, y){
22252         this.proxy.update(this.dragData.ddel.cloneNode(true));
22253         this.onStartDrag(x, y);
22254         return true;
22255     },
22256     
22257     /**
22258      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22259      */
22260     afterRepair : function(){
22261         if(Roo.enableFx){
22262             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22263         }
22264         this.dragging = false;
22265     },
22266
22267     /**
22268      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22269      * the XY of this.dragData.ddel
22270      * @param {EventObject} e The mouse up event
22271      * @return {Array} The xy location (e.g. [100, 200])
22272      */
22273     getRepairXY : function(e){
22274         return Roo.Element.fly(this.dragData.ddel).getXY();  
22275     }
22276 });/*
22277  * Based on:
22278  * Ext JS Library 1.1.1
22279  * Copyright(c) 2006-2007, Ext JS, LLC.
22280  *
22281  * Originally Released Under LGPL - original licence link has changed is not relivant.
22282  *
22283  * Fork - LGPL
22284  * <script type="text/javascript">
22285  */
22286 /**
22287  * @class Roo.dd.DropZone
22288  * @extends Roo.dd.DropTarget
22289  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22290  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22291  * @constructor
22292  * @param {String/HTMLElement/Element} el The container element
22293  * @param {Object} config
22294  */
22295 Roo.dd.DropZone = function(el, config){
22296     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22297 };
22298
22299 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22300     /**
22301      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22302      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22303      * provide your own custom lookup.
22304      * @param {Event} e The event
22305      * @return {Object} data The custom data
22306      */
22307     getTargetFromEvent : function(e){
22308         return Roo.dd.Registry.getTargetFromEvent(e);
22309     },
22310
22311     /**
22312      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22313      * that it has registered.  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     onNodeEnter : function(n, dd, e, data){
22322         
22323     },
22324
22325     /**
22326      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22327      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22328      * overridden to provide the proper feedback.
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 {String} status The CSS class that communicates the drop status back to the source so that the
22335      * underlying {@link Roo.dd.StatusProxy} can be updated
22336      */
22337     onNodeOver : function(n, dd, e, data){
22338         return this.dropAllowed;
22339     },
22340
22341     /**
22342      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22343      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22344      * node-specific processing if necessary.
22345      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22346      * {@link #getTargetFromEvent} for this node)
22347      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22348      * @param {Event} e The event
22349      * @param {Object} data An object containing arbitrary data supplied by the drag source
22350      */
22351     onNodeOut : function(n, dd, e, data){
22352         
22353     },
22354
22355     /**
22356      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22357      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22358      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22359      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22360      * {@link #getTargetFromEvent} for this node)
22361      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22362      * @param {Event} e The event
22363      * @param {Object} data An object containing arbitrary data supplied by the drag source
22364      * @return {Boolean} True if the drop was valid, else false
22365      */
22366     onNodeDrop : function(n, dd, e, data){
22367         return false;
22368     },
22369
22370     /**
22371      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22372      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22373      * it should be overridden to provide the proper feedback if necessary.
22374      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22375      * @param {Event} e The event
22376      * @param {Object} data An object containing arbitrary data supplied by the drag source
22377      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22378      * underlying {@link Roo.dd.StatusProxy} can be updated
22379      */
22380     onContainerOver : function(dd, e, data){
22381         return this.dropNotAllowed;
22382     },
22383
22384     /**
22385      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22386      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22387      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22388      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22389      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22390      * @param {Event} e The event
22391      * @param {Object} data An object containing arbitrary data supplied by the drag source
22392      * @return {Boolean} True if the drop was valid, else false
22393      */
22394     onContainerDrop : function(dd, e, data){
22395         return false;
22396     },
22397
22398     /**
22399      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22400      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22401      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22402      * you should override this method and provide a custom implementation.
22403      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22404      * @param {Event} e The event
22405      * @param {Object} data An object containing arbitrary data supplied by the drag source
22406      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22407      * underlying {@link Roo.dd.StatusProxy} can be updated
22408      */
22409     notifyEnter : function(dd, e, data){
22410         return this.dropNotAllowed;
22411     },
22412
22413     /**
22414      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22415      * This method will be called on every mouse movement while the drag source is over the drop zone.
22416      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22417      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22418      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22419      * registered node, it will call {@link #onContainerOver}.
22420      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22421      * @param {Event} e The event
22422      * @param {Object} data An object containing arbitrary data supplied by the drag source
22423      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22424      * underlying {@link Roo.dd.StatusProxy} can be updated
22425      */
22426     notifyOver : function(dd, e, data){
22427         var n = this.getTargetFromEvent(e);
22428         if(!n){ // not over valid drop target
22429             if(this.lastOverNode){
22430                 this.onNodeOut(this.lastOverNode, dd, e, data);
22431                 this.lastOverNode = null;
22432             }
22433             return this.onContainerOver(dd, e, data);
22434         }
22435         if(this.lastOverNode != n){
22436             if(this.lastOverNode){
22437                 this.onNodeOut(this.lastOverNode, dd, e, data);
22438             }
22439             this.onNodeEnter(n, dd, e, data);
22440             this.lastOverNode = n;
22441         }
22442         return this.onNodeOver(n, dd, e, data);
22443     },
22444
22445     /**
22446      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22447      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22448      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22449      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22450      * @param {Event} e The event
22451      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22452      */
22453     notifyOut : function(dd, e, data){
22454         if(this.lastOverNode){
22455             this.onNodeOut(this.lastOverNode, dd, e, data);
22456             this.lastOverNode = null;
22457         }
22458     },
22459
22460     /**
22461      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22462      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22463      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22464      * otherwise it will call {@link #onContainerDrop}.
22465      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22466      * @param {Event} e The event
22467      * @param {Object} data An object containing arbitrary data supplied by the drag source
22468      * @return {Boolean} True if the drop was valid, else false
22469      */
22470     notifyDrop : function(dd, e, data){
22471         if(this.lastOverNode){
22472             this.onNodeOut(this.lastOverNode, dd, e, data);
22473             this.lastOverNode = null;
22474         }
22475         var n = this.getTargetFromEvent(e);
22476         return n ?
22477             this.onNodeDrop(n, dd, e, data) :
22478             this.onContainerDrop(dd, e, data);
22479     },
22480
22481     // private
22482     triggerCacheRefresh : function(){
22483         Roo.dd.DDM.refreshCache(this.groups);
22484     }  
22485 });/*
22486  * Based on:
22487  * Ext JS Library 1.1.1
22488  * Copyright(c) 2006-2007, Ext JS, LLC.
22489  *
22490  * Originally Released Under LGPL - original licence link has changed is not relivant.
22491  *
22492  * Fork - LGPL
22493  * <script type="text/javascript">
22494  */
22495
22496
22497 /**
22498  * @class Roo.data.SortTypes
22499  * @singleton
22500  * Defines the default sorting (casting?) comparison functions used when sorting data.
22501  */
22502 Roo.data.SortTypes = {
22503     /**
22504      * Default sort that does nothing
22505      * @param {Mixed} s The value being converted
22506      * @return {Mixed} The comparison value
22507      */
22508     none : function(s){
22509         return s;
22510     },
22511     
22512     /**
22513      * The regular expression used to strip tags
22514      * @type {RegExp}
22515      * @property
22516      */
22517     stripTagsRE : /<\/?[^>]+>/gi,
22518     
22519     /**
22520      * Strips all HTML tags to sort on text only
22521      * @param {Mixed} s The value being converted
22522      * @return {String} The comparison value
22523      */
22524     asText : function(s){
22525         return String(s).replace(this.stripTagsRE, "");
22526     },
22527     
22528     /**
22529      * Strips all HTML tags to sort on text only - Case insensitive
22530      * @param {Mixed} s The value being converted
22531      * @return {String} The comparison value
22532      */
22533     asUCText : function(s){
22534         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22535     },
22536     
22537     /**
22538      * Case insensitive string
22539      * @param {Mixed} s The value being converted
22540      * @return {String} The comparison value
22541      */
22542     asUCString : function(s) {
22543         return String(s).toUpperCase();
22544     },
22545     
22546     /**
22547      * Date sorting
22548      * @param {Mixed} s The value being converted
22549      * @return {Number} The comparison value
22550      */
22551     asDate : function(s) {
22552         if(!s){
22553             return 0;
22554         }
22555         if(s instanceof Date){
22556             return s.getTime();
22557         }
22558         return Date.parse(String(s));
22559     },
22560     
22561     /**
22562      * Float sorting
22563      * @param {Mixed} s The value being converted
22564      * @return {Float} The comparison value
22565      */
22566     asFloat : function(s) {
22567         var val = parseFloat(String(s).replace(/,/g, ""));
22568         if(isNaN(val)) {
22569             val = 0;
22570         }
22571         return val;
22572     },
22573     
22574     /**
22575      * Integer sorting
22576      * @param {Mixed} s The value being converted
22577      * @return {Number} The comparison value
22578      */
22579     asInt : function(s) {
22580         var val = parseInt(String(s).replace(/,/g, ""));
22581         if(isNaN(val)) {
22582             val = 0;
22583         }
22584         return val;
22585     }
22586 };/*
22587  * Based on:
22588  * Ext JS Library 1.1.1
22589  * Copyright(c) 2006-2007, Ext JS, LLC.
22590  *
22591  * Originally Released Under LGPL - original licence link has changed is not relivant.
22592  *
22593  * Fork - LGPL
22594  * <script type="text/javascript">
22595  */
22596
22597 /**
22598 * @class Roo.data.Record
22599  * Instances of this class encapsulate both record <em>definition</em> information, and record
22600  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22601  * to access Records cached in an {@link Roo.data.Store} object.<br>
22602  * <p>
22603  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22604  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22605  * objects.<br>
22606  * <p>
22607  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22608  * @constructor
22609  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22610  * {@link #create}. The parameters are the same.
22611  * @param {Array} data An associative Array of data values keyed by the field name.
22612  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22613  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22614  * not specified an integer id is generated.
22615  */
22616 Roo.data.Record = function(data, id){
22617     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22618     this.data = data;
22619 };
22620
22621 /**
22622  * Generate a constructor for a specific record layout.
22623  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22624  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22625  * Each field definition object may contain the following properties: <ul>
22626  * <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,
22627  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22628  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22629  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22630  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22631  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22632  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22633  * this may be omitted.</p></li>
22634  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22635  * <ul><li>auto (Default, implies no conversion)</li>
22636  * <li>string</li>
22637  * <li>int</li>
22638  * <li>float</li>
22639  * <li>boolean</li>
22640  * <li>date</li></ul></p></li>
22641  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22642  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22643  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22644  * by the Reader into an object that will be stored in the Record. It is passed the
22645  * following parameters:<ul>
22646  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22647  * </ul></p></li>
22648  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22649  * </ul>
22650  * <br>usage:<br><pre><code>
22651 var TopicRecord = Roo.data.Record.create(
22652     {name: 'title', mapping: 'topic_title'},
22653     {name: 'author', mapping: 'username'},
22654     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22655     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22656     {name: 'lastPoster', mapping: 'user2'},
22657     {name: 'excerpt', mapping: 'post_text'}
22658 );
22659
22660 var myNewRecord = new TopicRecord({
22661     title: 'Do my job please',
22662     author: 'noobie',
22663     totalPosts: 1,
22664     lastPost: new Date(),
22665     lastPoster: 'Animal',
22666     excerpt: 'No way dude!'
22667 });
22668 myStore.add(myNewRecord);
22669 </code></pre>
22670  * @method create
22671  * @static
22672  */
22673 Roo.data.Record.create = function(o){
22674     var f = function(){
22675         f.superclass.constructor.apply(this, arguments);
22676     };
22677     Roo.extend(f, Roo.data.Record);
22678     var p = f.prototype;
22679     p.fields = new Roo.util.MixedCollection(false, function(field){
22680         return field.name;
22681     });
22682     for(var i = 0, len = o.length; i < len; i++){
22683         p.fields.add(new Roo.data.Field(o[i]));
22684     }
22685     f.getField = function(name){
22686         return p.fields.get(name);  
22687     };
22688     return f;
22689 };
22690
22691 Roo.data.Record.AUTO_ID = 1000;
22692 Roo.data.Record.EDIT = 'edit';
22693 Roo.data.Record.REJECT = 'reject';
22694 Roo.data.Record.COMMIT = 'commit';
22695
22696 Roo.data.Record.prototype = {
22697     /**
22698      * Readonly flag - true if this record has been modified.
22699      * @type Boolean
22700      */
22701     dirty : false,
22702     editing : false,
22703     error: null,
22704     modified: null,
22705
22706     // private
22707     join : function(store){
22708         this.store = store;
22709     },
22710
22711     /**
22712      * Set the named field to the specified value.
22713      * @param {String} name The name of the field to set.
22714      * @param {Object} value The value to set the field to.
22715      */
22716     set : function(name, value){
22717         if(this.data[name] == value){
22718             return;
22719         }
22720         this.dirty = true;
22721         if(!this.modified){
22722             this.modified = {};
22723         }
22724         if(typeof this.modified[name] == 'undefined'){
22725             this.modified[name] = this.data[name];
22726         }
22727         this.data[name] = value;
22728         if(!this.editing && this.store){
22729             this.store.afterEdit(this);
22730         }       
22731     },
22732
22733     /**
22734      * Get the value of the named field.
22735      * @param {String} name The name of the field to get the value of.
22736      * @return {Object} The value of the field.
22737      */
22738     get : function(name){
22739         return this.data[name]; 
22740     },
22741
22742     // private
22743     beginEdit : function(){
22744         this.editing = true;
22745         this.modified = {}; 
22746     },
22747
22748     // private
22749     cancelEdit : function(){
22750         this.editing = false;
22751         delete this.modified;
22752     },
22753
22754     // private
22755     endEdit : function(){
22756         this.editing = false;
22757         if(this.dirty && this.store){
22758             this.store.afterEdit(this);
22759         }
22760     },
22761
22762     /**
22763      * Usually called by the {@link Roo.data.Store} which owns the Record.
22764      * Rejects all changes made to the Record since either creation, or the last commit operation.
22765      * Modified fields are reverted to their original values.
22766      * <p>
22767      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22768      * of reject operations.
22769      */
22770     reject : function(){
22771         var m = this.modified;
22772         for(var n in m){
22773             if(typeof m[n] != "function"){
22774                 this.data[n] = m[n];
22775             }
22776         }
22777         this.dirty = false;
22778         delete this.modified;
22779         this.editing = false;
22780         if(this.store){
22781             this.store.afterReject(this);
22782         }
22783     },
22784
22785     /**
22786      * Usually called by the {@link Roo.data.Store} which owns the Record.
22787      * Commits all changes made to the Record since either creation, or the last commit operation.
22788      * <p>
22789      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22790      * of commit operations.
22791      */
22792     commit : function(){
22793         this.dirty = false;
22794         delete this.modified;
22795         this.editing = false;
22796         if(this.store){
22797             this.store.afterCommit(this);
22798         }
22799     },
22800
22801     // private
22802     hasError : function(){
22803         return this.error != null;
22804     },
22805
22806     // private
22807     clearError : function(){
22808         this.error = null;
22809     },
22810
22811     /**
22812      * Creates a copy of this record.
22813      * @param {String} id (optional) A new record id if you don't want to use this record's id
22814      * @return {Record}
22815      */
22816     copy : function(newId) {
22817         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22818     }
22819 };/*
22820  * Based on:
22821  * Ext JS Library 1.1.1
22822  * Copyright(c) 2006-2007, Ext JS, LLC.
22823  *
22824  * Originally Released Under LGPL - original licence link has changed is not relivant.
22825  *
22826  * Fork - LGPL
22827  * <script type="text/javascript">
22828  */
22829
22830
22831
22832 /**
22833  * @class Roo.data.Store
22834  * @extends Roo.util.Observable
22835  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22836  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22837  * <p>
22838  * 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
22839  * has no knowledge of the format of the data returned by the Proxy.<br>
22840  * <p>
22841  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22842  * instances from the data object. These records are cached and made available through accessor functions.
22843  * @constructor
22844  * Creates a new Store.
22845  * @param {Object} config A config object containing the objects needed for the Store to access data,
22846  * and read the data into Records.
22847  */
22848 Roo.data.Store = function(config){
22849     this.data = new Roo.util.MixedCollection(false);
22850     this.data.getKey = function(o){
22851         return o.id;
22852     };
22853     this.baseParams = {};
22854     // private
22855     this.paramNames = {
22856         "start" : "start",
22857         "limit" : "limit",
22858         "sort" : "sort",
22859         "dir" : "dir",
22860         "multisort" : "_multisort"
22861     };
22862
22863     if(config && config.data){
22864         this.inlineData = config.data;
22865         delete config.data;
22866     }
22867
22868     Roo.apply(this, config);
22869     
22870     if(this.reader){ // reader passed
22871         this.reader = Roo.factory(this.reader, Roo.data);
22872         this.reader.xmodule = this.xmodule || false;
22873         if(!this.recordType){
22874             this.recordType = this.reader.recordType;
22875         }
22876         if(this.reader.onMetaChange){
22877             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22878         }
22879     }
22880
22881     if(this.recordType){
22882         this.fields = this.recordType.prototype.fields;
22883     }
22884     this.modified = [];
22885
22886     this.addEvents({
22887         /**
22888          * @event datachanged
22889          * Fires when the data cache has changed, and a widget which is using this Store
22890          * as a Record cache should refresh its view.
22891          * @param {Store} this
22892          */
22893         datachanged : true,
22894         /**
22895          * @event metachange
22896          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22897          * @param {Store} this
22898          * @param {Object} meta The JSON metadata
22899          */
22900         metachange : true,
22901         /**
22902          * @event add
22903          * Fires when Records have been added to the Store
22904          * @param {Store} this
22905          * @param {Roo.data.Record[]} records The array of Records added
22906          * @param {Number} index The index at which the record(s) were added
22907          */
22908         add : true,
22909         /**
22910          * @event remove
22911          * Fires when a Record has been removed from the Store
22912          * @param {Store} this
22913          * @param {Roo.data.Record} record The Record that was removed
22914          * @param {Number} index The index at which the record was removed
22915          */
22916         remove : true,
22917         /**
22918          * @event update
22919          * Fires when a Record has been updated
22920          * @param {Store} this
22921          * @param {Roo.data.Record} record The Record that was updated
22922          * @param {String} operation The update operation being performed.  Value may be one of:
22923          * <pre><code>
22924  Roo.data.Record.EDIT
22925  Roo.data.Record.REJECT
22926  Roo.data.Record.COMMIT
22927          * </code></pre>
22928          */
22929         update : true,
22930         /**
22931          * @event clear
22932          * Fires when the data cache has been cleared.
22933          * @param {Store} this
22934          */
22935         clear : true,
22936         /**
22937          * @event beforeload
22938          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22939          * the load action will be canceled.
22940          * @param {Store} this
22941          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22942          */
22943         beforeload : true,
22944         /**
22945          * @event beforeloadadd
22946          * Fires after a new set of Records has been loaded.
22947          * @param {Store} this
22948          * @param {Roo.data.Record[]} records The Records that were loaded
22949          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22950          */
22951         beforeloadadd : true,
22952         /**
22953          * @event load
22954          * Fires after a new set of Records has been loaded, before they are added to the store.
22955          * @param {Store} this
22956          * @param {Roo.data.Record[]} records The Records that were loaded
22957          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22958          * @params {Object} return from reader
22959          */
22960         load : true,
22961         /**
22962          * @event loadexception
22963          * Fires if an exception occurs in the Proxy during loading.
22964          * Called with the signature of the Proxy's "loadexception" event.
22965          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22966          * 
22967          * @param {Proxy} 
22968          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22969          * @param {Object} load options 
22970          * @param {Object} jsonData from your request (normally this contains the Exception)
22971          */
22972         loadexception : true
22973     });
22974     
22975     if(this.proxy){
22976         this.proxy = Roo.factory(this.proxy, Roo.data);
22977         this.proxy.xmodule = this.xmodule || false;
22978         this.relayEvents(this.proxy,  ["loadexception"]);
22979     }
22980     this.sortToggle = {};
22981     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22982
22983     Roo.data.Store.superclass.constructor.call(this);
22984
22985     if(this.inlineData){
22986         this.loadData(this.inlineData);
22987         delete this.inlineData;
22988     }
22989 };
22990
22991 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22992      /**
22993     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22994     * without a remote query - used by combo/forms at present.
22995     */
22996     
22997     /**
22998     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22999     */
23000     /**
23001     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23002     */
23003     /**
23004     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23005     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23006     */
23007     /**
23008     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23009     * on any HTTP request
23010     */
23011     /**
23012     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23013     */
23014     /**
23015     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23016     */
23017     multiSort: false,
23018     /**
23019     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23020     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23021     */
23022     remoteSort : false,
23023
23024     /**
23025     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23026      * loaded or when a record is removed. (defaults to false).
23027     */
23028     pruneModifiedRecords : false,
23029
23030     // private
23031     lastOptions : null,
23032
23033     /**
23034      * Add Records to the Store and fires the add event.
23035      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23036      */
23037     add : function(records){
23038         records = [].concat(records);
23039         for(var i = 0, len = records.length; i < len; i++){
23040             records[i].join(this);
23041         }
23042         var index = this.data.length;
23043         this.data.addAll(records);
23044         this.fireEvent("add", this, records, index);
23045     },
23046
23047     /**
23048      * Remove a Record from the Store and fires the remove event.
23049      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23050      */
23051     remove : function(record){
23052         var index = this.data.indexOf(record);
23053         this.data.removeAt(index);
23054  
23055         if(this.pruneModifiedRecords){
23056             this.modified.remove(record);
23057         }
23058         this.fireEvent("remove", this, record, index);
23059     },
23060
23061     /**
23062      * Remove all Records from the Store and fires the clear event.
23063      */
23064     removeAll : function(){
23065         this.data.clear();
23066         if(this.pruneModifiedRecords){
23067             this.modified = [];
23068         }
23069         this.fireEvent("clear", this);
23070     },
23071
23072     /**
23073      * Inserts Records to the Store at the given index and fires the add event.
23074      * @param {Number} index The start index at which to insert the passed Records.
23075      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23076      */
23077     insert : function(index, records){
23078         records = [].concat(records);
23079         for(var i = 0, len = records.length; i < len; i++){
23080             this.data.insert(index, records[i]);
23081             records[i].join(this);
23082         }
23083         this.fireEvent("add", this, records, index);
23084     },
23085
23086     /**
23087      * Get the index within the cache of the passed Record.
23088      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23089      * @return {Number} The index of the passed Record. Returns -1 if not found.
23090      */
23091     indexOf : function(record){
23092         return this.data.indexOf(record);
23093     },
23094
23095     /**
23096      * Get the index within the cache of the Record with the passed id.
23097      * @param {String} id The id of the Record to find.
23098      * @return {Number} The index of the Record. Returns -1 if not found.
23099      */
23100     indexOfId : function(id){
23101         return this.data.indexOfKey(id);
23102     },
23103
23104     /**
23105      * Get the Record with the specified id.
23106      * @param {String} id The id of the Record to find.
23107      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23108      */
23109     getById : function(id){
23110         return this.data.key(id);
23111     },
23112
23113     /**
23114      * Get the Record at the specified index.
23115      * @param {Number} index The index of the Record to find.
23116      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23117      */
23118     getAt : function(index){
23119         return this.data.itemAt(index);
23120     },
23121
23122     /**
23123      * Returns a range of Records between specified indices.
23124      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23125      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23126      * @return {Roo.data.Record[]} An array of Records
23127      */
23128     getRange : function(start, end){
23129         return this.data.getRange(start, end);
23130     },
23131
23132     // private
23133     storeOptions : function(o){
23134         o = Roo.apply({}, o);
23135         delete o.callback;
23136         delete o.scope;
23137         this.lastOptions = o;
23138     },
23139
23140     /**
23141      * Loads the Record cache from the configured Proxy using the configured Reader.
23142      * <p>
23143      * If using remote paging, then the first load call must specify the <em>start</em>
23144      * and <em>limit</em> properties in the options.params property to establish the initial
23145      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23146      * <p>
23147      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23148      * and this call will return before the new data has been loaded. Perform any post-processing
23149      * in a callback function, or in a "load" event handler.</strong>
23150      * <p>
23151      * @param {Object} options An object containing properties which control loading options:<ul>
23152      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23153      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23154      * passed the following arguments:<ul>
23155      * <li>r : Roo.data.Record[]</li>
23156      * <li>options: Options object from the load call</li>
23157      * <li>success: Boolean success indicator</li></ul></li>
23158      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23159      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23160      * </ul>
23161      */
23162     load : function(options){
23163         options = options || {};
23164         if(this.fireEvent("beforeload", this, options) !== false){
23165             this.storeOptions(options);
23166             var p = Roo.apply(options.params || {}, this.baseParams);
23167             // if meta was not loaded from remote source.. try requesting it.
23168             if (!this.reader.metaFromRemote) {
23169                 p._requestMeta = 1;
23170             }
23171             if(this.sortInfo && this.remoteSort){
23172                 var pn = this.paramNames;
23173                 p[pn["sort"]] = this.sortInfo.field;
23174                 p[pn["dir"]] = this.sortInfo.direction;
23175             }
23176             if (this.multiSort) {
23177                 var pn = this.paramNames;
23178                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23179             }
23180             
23181             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23182         }
23183     },
23184
23185     /**
23186      * Reloads the Record cache from the configured Proxy using the configured Reader and
23187      * the options from the last load operation performed.
23188      * @param {Object} options (optional) An object containing properties which may override the options
23189      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23190      * the most recently used options are reused).
23191      */
23192     reload : function(options){
23193         this.load(Roo.applyIf(options||{}, this.lastOptions));
23194     },
23195
23196     // private
23197     // Called as a callback by the Reader during a load operation.
23198     loadRecords : function(o, options, success){
23199         if(!o || success === false){
23200             if(success !== false){
23201                 this.fireEvent("load", this, [], options, o);
23202             }
23203             if(options.callback){
23204                 options.callback.call(options.scope || this, [], options, false);
23205             }
23206             return;
23207         }
23208         // if data returned failure - throw an exception.
23209         if (o.success === false) {
23210             // show a message if no listener is registered.
23211             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23212                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23213             }
23214             // loadmask wil be hooked into this..
23215             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23216             return;
23217         }
23218         var r = o.records, t = o.totalRecords || r.length;
23219         
23220         this.fireEvent("beforeloadadd", this, r, options, o);
23221         
23222         if(!options || options.add !== true){
23223             if(this.pruneModifiedRecords){
23224                 this.modified = [];
23225             }
23226             for(var i = 0, len = r.length; i < len; i++){
23227                 r[i].join(this);
23228             }
23229             if(this.snapshot){
23230                 this.data = this.snapshot;
23231                 delete this.snapshot;
23232             }
23233             this.data.clear();
23234             this.data.addAll(r);
23235             this.totalLength = t;
23236             this.applySort();
23237             this.fireEvent("datachanged", this);
23238         }else{
23239             this.totalLength = Math.max(t, this.data.length+r.length);
23240             this.add(r);
23241         }
23242         
23243         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23244                 
23245             var e = new Roo.data.Record({});
23246
23247             e.set(this.parent.displayField, this.parent.emptyTitle);
23248             e.set(this.parent.valueField, '');
23249
23250             this.insert(0, e);
23251         }
23252             
23253         this.fireEvent("load", this, r, options, o);
23254         if(options.callback){
23255             options.callback.call(options.scope || this, r, options, true);
23256         }
23257     },
23258
23259
23260     /**
23261      * Loads data from a passed data block. A Reader which understands the format of the data
23262      * must have been configured in the constructor.
23263      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23264      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23265      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23266      */
23267     loadData : function(o, append){
23268         var r = this.reader.readRecords(o);
23269         this.loadRecords(r, {add: append}, true);
23270     },
23271
23272     /**
23273      * Gets the number of cached records.
23274      * <p>
23275      * <em>If using paging, this may not be the total size of the dataset. If the data object
23276      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23277      * the data set size</em>
23278      */
23279     getCount : function(){
23280         return this.data.length || 0;
23281     },
23282
23283     /**
23284      * Gets the total number of records in the dataset as returned by the server.
23285      * <p>
23286      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23287      * the dataset size</em>
23288      */
23289     getTotalCount : function(){
23290         return this.totalLength || 0;
23291     },
23292
23293     /**
23294      * Returns the sort state of the Store as an object with two properties:
23295      * <pre><code>
23296  field {String} The name of the field by which the Records are sorted
23297  direction {String} The sort order, "ASC" or "DESC"
23298      * </code></pre>
23299      */
23300     getSortState : function(){
23301         return this.sortInfo;
23302     },
23303
23304     // private
23305     applySort : function(){
23306         if(this.sortInfo && !this.remoteSort){
23307             var s = this.sortInfo, f = s.field;
23308             var st = this.fields.get(f).sortType;
23309             var fn = function(r1, r2){
23310                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23311                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23312             };
23313             this.data.sort(s.direction, fn);
23314             if(this.snapshot && this.snapshot != this.data){
23315                 this.snapshot.sort(s.direction, fn);
23316             }
23317         }
23318     },
23319
23320     /**
23321      * Sets the default sort column and order to be used by the next load operation.
23322      * @param {String} fieldName The name of the field to sort by.
23323      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23324      */
23325     setDefaultSort : function(field, dir){
23326         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23327     },
23328
23329     /**
23330      * Sort the Records.
23331      * If remote sorting is used, the sort is performed on the server, and the cache is
23332      * reloaded. If local sorting is used, the cache is sorted internally.
23333      * @param {String} fieldName The name of the field to sort by.
23334      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23335      */
23336     sort : function(fieldName, dir){
23337         var f = this.fields.get(fieldName);
23338         if(!dir){
23339             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23340             
23341             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23342                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23343             }else{
23344                 dir = f.sortDir;
23345             }
23346         }
23347         this.sortToggle[f.name] = dir;
23348         this.sortInfo = {field: f.name, direction: dir};
23349         if(!this.remoteSort){
23350             this.applySort();
23351             this.fireEvent("datachanged", this);
23352         }else{
23353             this.load(this.lastOptions);
23354         }
23355     },
23356
23357     /**
23358      * Calls the specified function for each of the Records in the cache.
23359      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23360      * Returning <em>false</em> aborts and exits the iteration.
23361      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23362      */
23363     each : function(fn, scope){
23364         this.data.each(fn, scope);
23365     },
23366
23367     /**
23368      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23369      * (e.g., during paging).
23370      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23371      */
23372     getModifiedRecords : function(){
23373         return this.modified;
23374     },
23375
23376     // private
23377     createFilterFn : function(property, value, anyMatch){
23378         if(!value.exec){ // not a regex
23379             value = String(value);
23380             if(value.length == 0){
23381                 return false;
23382             }
23383             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23384         }
23385         return function(r){
23386             return value.test(r.data[property]);
23387         };
23388     },
23389
23390     /**
23391      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23392      * @param {String} property A field on your records
23393      * @param {Number} start The record index to start at (defaults to 0)
23394      * @param {Number} end The last record index to include (defaults to length - 1)
23395      * @return {Number} The sum
23396      */
23397     sum : function(property, start, end){
23398         var rs = this.data.items, v = 0;
23399         start = start || 0;
23400         end = (end || end === 0) ? end : rs.length-1;
23401
23402         for(var i = start; i <= end; i++){
23403             v += (rs[i].data[property] || 0);
23404         }
23405         return v;
23406     },
23407
23408     /**
23409      * Filter the records by a specified property.
23410      * @param {String} field A field on your records
23411      * @param {String/RegExp} value Either a string that the field
23412      * should start with or a RegExp to test against the field
23413      * @param {Boolean} anyMatch True to match any part not just the beginning
23414      */
23415     filter : function(property, value, anyMatch){
23416         var fn = this.createFilterFn(property, value, anyMatch);
23417         return fn ? this.filterBy(fn) : this.clearFilter();
23418     },
23419
23420     /**
23421      * Filter by a function. The specified function will be called with each
23422      * record in this data source. If the function returns true the record is included,
23423      * otherwise it is filtered.
23424      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23425      * @param {Object} scope (optional) The scope of the function (defaults to this)
23426      */
23427     filterBy : function(fn, scope){
23428         this.snapshot = this.snapshot || this.data;
23429         this.data = this.queryBy(fn, scope||this);
23430         this.fireEvent("datachanged", this);
23431     },
23432
23433     /**
23434      * Query the records by a specified property.
23435      * @param {String} field A field on your records
23436      * @param {String/RegExp} value Either a string that the field
23437      * should start with or a RegExp to test against the field
23438      * @param {Boolean} anyMatch True to match any part not just the beginning
23439      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23440      */
23441     query : function(property, value, anyMatch){
23442         var fn = this.createFilterFn(property, value, anyMatch);
23443         return fn ? this.queryBy(fn) : this.data.clone();
23444     },
23445
23446     /**
23447      * Query by a function. The specified function will be called with each
23448      * record in this data source. If the function returns true the record is included
23449      * in the results.
23450      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23451      * @param {Object} scope (optional) The scope of the function (defaults to this)
23452       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23453      **/
23454     queryBy : function(fn, scope){
23455         var data = this.snapshot || this.data;
23456         return data.filterBy(fn, scope||this);
23457     },
23458
23459     /**
23460      * Collects unique values for a particular dataIndex from this store.
23461      * @param {String} dataIndex The property to collect
23462      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23463      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23464      * @return {Array} An array of the unique values
23465      **/
23466     collect : function(dataIndex, allowNull, bypassFilter){
23467         var d = (bypassFilter === true && this.snapshot) ?
23468                 this.snapshot.items : this.data.items;
23469         var v, sv, r = [], l = {};
23470         for(var i = 0, len = d.length; i < len; i++){
23471             v = d[i].data[dataIndex];
23472             sv = String(v);
23473             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23474                 l[sv] = true;
23475                 r[r.length] = v;
23476             }
23477         }
23478         return r;
23479     },
23480
23481     /**
23482      * Revert to a view of the Record cache with no filtering applied.
23483      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23484      */
23485     clearFilter : function(suppressEvent){
23486         if(this.snapshot && this.snapshot != this.data){
23487             this.data = this.snapshot;
23488             delete this.snapshot;
23489             if(suppressEvent !== true){
23490                 this.fireEvent("datachanged", this);
23491             }
23492         }
23493     },
23494
23495     // private
23496     afterEdit : function(record){
23497         if(this.modified.indexOf(record) == -1){
23498             this.modified.push(record);
23499         }
23500         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23501     },
23502     
23503     // private
23504     afterReject : function(record){
23505         this.modified.remove(record);
23506         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23507     },
23508
23509     // private
23510     afterCommit : function(record){
23511         this.modified.remove(record);
23512         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23513     },
23514
23515     /**
23516      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23517      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23518      */
23519     commitChanges : function(){
23520         var m = this.modified.slice(0);
23521         this.modified = [];
23522         for(var i = 0, len = m.length; i < len; i++){
23523             m[i].commit();
23524         }
23525     },
23526
23527     /**
23528      * Cancel outstanding changes on all changed records.
23529      */
23530     rejectChanges : function(){
23531         var m = this.modified.slice(0);
23532         this.modified = [];
23533         for(var i = 0, len = m.length; i < len; i++){
23534             m[i].reject();
23535         }
23536     },
23537
23538     onMetaChange : function(meta, rtype, o){
23539         this.recordType = rtype;
23540         this.fields = rtype.prototype.fields;
23541         delete this.snapshot;
23542         this.sortInfo = meta.sortInfo || this.sortInfo;
23543         this.modified = [];
23544         this.fireEvent('metachange', this, this.reader.meta);
23545     },
23546     
23547     moveIndex : function(data, type)
23548     {
23549         var index = this.indexOf(data);
23550         
23551         var newIndex = index + type;
23552         
23553         this.remove(data);
23554         
23555         this.insert(newIndex, data);
23556         
23557     }
23558 });/*
23559  * Based on:
23560  * Ext JS Library 1.1.1
23561  * Copyright(c) 2006-2007, Ext JS, LLC.
23562  *
23563  * Originally Released Under LGPL - original licence link has changed is not relivant.
23564  *
23565  * Fork - LGPL
23566  * <script type="text/javascript">
23567  */
23568
23569 /**
23570  * @class Roo.data.SimpleStore
23571  * @extends Roo.data.Store
23572  * Small helper class to make creating Stores from Array data easier.
23573  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23574  * @cfg {Array} fields An array of field definition objects, or field name strings.
23575  * @cfg {Object} an existing reader (eg. copied from another store)
23576  * @cfg {Array} data The multi-dimensional array of data
23577  * @constructor
23578  * @param {Object} config
23579  */
23580 Roo.data.SimpleStore = function(config)
23581 {
23582     Roo.data.SimpleStore.superclass.constructor.call(this, {
23583         isLocal : true,
23584         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
23585                 id: config.id
23586             },
23587             Roo.data.Record.create(config.fields)
23588         ),
23589         proxy : new Roo.data.MemoryProxy(config.data)
23590     });
23591     this.load();
23592 };
23593 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23594  * Based on:
23595  * Ext JS Library 1.1.1
23596  * Copyright(c) 2006-2007, Ext JS, LLC.
23597  *
23598  * Originally Released Under LGPL - original licence link has changed is not relivant.
23599  *
23600  * Fork - LGPL
23601  * <script type="text/javascript">
23602  */
23603
23604 /**
23605 /**
23606  * @extends Roo.data.Store
23607  * @class Roo.data.JsonStore
23608  * Small helper class to make creating Stores for JSON data easier. <br/>
23609 <pre><code>
23610 var store = new Roo.data.JsonStore({
23611     url: 'get-images.php',
23612     root: 'images',
23613     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23614 });
23615 </code></pre>
23616  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23617  * JsonReader and HttpProxy (unless inline data is provided).</b>
23618  * @cfg {Array} fields An array of field definition objects, or field name strings.
23619  * @constructor
23620  * @param {Object} config
23621  */
23622 Roo.data.JsonStore = function(c){
23623     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23624         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23625         reader: new Roo.data.JsonReader(c, c.fields)
23626     }));
23627 };
23628 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23629  * Based on:
23630  * Ext JS Library 1.1.1
23631  * Copyright(c) 2006-2007, Ext JS, LLC.
23632  *
23633  * Originally Released Under LGPL - original licence link has changed is not relivant.
23634  *
23635  * Fork - LGPL
23636  * <script type="text/javascript">
23637  */
23638
23639  
23640 Roo.data.Field = function(config){
23641     if(typeof config == "string"){
23642         config = {name: config};
23643     }
23644     Roo.apply(this, config);
23645     
23646     if(!this.type){
23647         this.type = "auto";
23648     }
23649     
23650     var st = Roo.data.SortTypes;
23651     // named sortTypes are supported, here we look them up
23652     if(typeof this.sortType == "string"){
23653         this.sortType = st[this.sortType];
23654     }
23655     
23656     // set default sortType for strings and dates
23657     if(!this.sortType){
23658         switch(this.type){
23659             case "string":
23660                 this.sortType = st.asUCString;
23661                 break;
23662             case "date":
23663                 this.sortType = st.asDate;
23664                 break;
23665             default:
23666                 this.sortType = st.none;
23667         }
23668     }
23669
23670     // define once
23671     var stripRe = /[\$,%]/g;
23672
23673     // prebuilt conversion function for this field, instead of
23674     // switching every time we're reading a value
23675     if(!this.convert){
23676         var cv, dateFormat = this.dateFormat;
23677         switch(this.type){
23678             case "":
23679             case "auto":
23680             case undefined:
23681                 cv = function(v){ return v; };
23682                 break;
23683             case "string":
23684                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23685                 break;
23686             case "int":
23687                 cv = function(v){
23688                     return v !== undefined && v !== null && v !== '' ?
23689                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23690                     };
23691                 break;
23692             case "float":
23693                 cv = function(v){
23694                     return v !== undefined && v !== null && v !== '' ?
23695                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23696                     };
23697                 break;
23698             case "bool":
23699             case "boolean":
23700                 cv = function(v){ return v === true || v === "true" || v == 1; };
23701                 break;
23702             case "date":
23703                 cv = function(v){
23704                     if(!v){
23705                         return '';
23706                     }
23707                     if(v instanceof Date){
23708                         return v;
23709                     }
23710                     if(dateFormat){
23711                         if(dateFormat == "timestamp"){
23712                             return new Date(v*1000);
23713                         }
23714                         return Date.parseDate(v, dateFormat);
23715                     }
23716                     var parsed = Date.parse(v);
23717                     return parsed ? new Date(parsed) : null;
23718                 };
23719              break;
23720             
23721         }
23722         this.convert = cv;
23723     }
23724 };
23725
23726 Roo.data.Field.prototype = {
23727     dateFormat: null,
23728     defaultValue: "",
23729     mapping: null,
23730     sortType : null,
23731     sortDir : "ASC"
23732 };/*
23733  * Based on:
23734  * Ext JS Library 1.1.1
23735  * Copyright(c) 2006-2007, Ext JS, LLC.
23736  *
23737  * Originally Released Under LGPL - original licence link has changed is not relivant.
23738  *
23739  * Fork - LGPL
23740  * <script type="text/javascript">
23741  */
23742  
23743 // Base class for reading structured data from a data source.  This class is intended to be
23744 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23745
23746 /**
23747  * @class Roo.data.DataReader
23748  * Base class for reading structured data from a data source.  This class is intended to be
23749  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23750  */
23751
23752 Roo.data.DataReader = function(meta, recordType){
23753     
23754     this.meta = meta;
23755     
23756     this.recordType = recordType instanceof Array ? 
23757         Roo.data.Record.create(recordType) : recordType;
23758 };
23759
23760 Roo.data.DataReader.prototype = {
23761     
23762     
23763     readerType : 'Data',
23764      /**
23765      * Create an empty record
23766      * @param {Object} data (optional) - overlay some values
23767      * @return {Roo.data.Record} record created.
23768      */
23769     newRow :  function(d) {
23770         var da =  {};
23771         this.recordType.prototype.fields.each(function(c) {
23772             switch( c.type) {
23773                 case 'int' : da[c.name] = 0; break;
23774                 case 'date' : da[c.name] = new Date(); break;
23775                 case 'float' : da[c.name] = 0.0; break;
23776                 case 'boolean' : da[c.name] = false; break;
23777                 default : da[c.name] = ""; break;
23778             }
23779             
23780         });
23781         return new this.recordType(Roo.apply(da, d));
23782     }
23783     
23784     
23785 };/*
23786  * Based on:
23787  * Ext JS Library 1.1.1
23788  * Copyright(c) 2006-2007, Ext JS, LLC.
23789  *
23790  * Originally Released Under LGPL - original licence link has changed is not relivant.
23791  *
23792  * Fork - LGPL
23793  * <script type="text/javascript">
23794  */
23795
23796 /**
23797  * @class Roo.data.DataProxy
23798  * @extends Roo.data.Observable
23799  * This class is an abstract base class for implementations which provide retrieval of
23800  * unformatted data objects.<br>
23801  * <p>
23802  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23803  * (of the appropriate type which knows how to parse the data object) to provide a block of
23804  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23805  * <p>
23806  * Custom implementations must implement the load method as described in
23807  * {@link Roo.data.HttpProxy#load}.
23808  */
23809 Roo.data.DataProxy = function(){
23810     this.addEvents({
23811         /**
23812          * @event beforeload
23813          * Fires before a network request is made to retrieve a data object.
23814          * @param {Object} This DataProxy object.
23815          * @param {Object} params The params parameter to the load function.
23816          */
23817         beforeload : true,
23818         /**
23819          * @event load
23820          * Fires before the load method's callback is called.
23821          * @param {Object} This DataProxy object.
23822          * @param {Object} o The data object.
23823          * @param {Object} arg The callback argument object passed to the load function.
23824          */
23825         load : true,
23826         /**
23827          * @event loadexception
23828          * Fires if an Exception occurs during data retrieval.
23829          * @param {Object} This DataProxy object.
23830          * @param {Object} o The data object.
23831          * @param {Object} arg The callback argument object passed to the load function.
23832          * @param {Object} e The Exception.
23833          */
23834         loadexception : true
23835     });
23836     Roo.data.DataProxy.superclass.constructor.call(this);
23837 };
23838
23839 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23840
23841     /**
23842      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23843      */
23844 /*
23845  * Based on:
23846  * Ext JS Library 1.1.1
23847  * Copyright(c) 2006-2007, Ext JS, LLC.
23848  *
23849  * Originally Released Under LGPL - original licence link has changed is not relivant.
23850  *
23851  * Fork - LGPL
23852  * <script type="text/javascript">
23853  */
23854 /**
23855  * @class Roo.data.MemoryProxy
23856  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23857  * to the Reader when its load method is called.
23858  * @constructor
23859  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23860  */
23861 Roo.data.MemoryProxy = function(data){
23862     if (data.data) {
23863         data = data.data;
23864     }
23865     Roo.data.MemoryProxy.superclass.constructor.call(this);
23866     this.data = data;
23867 };
23868
23869 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23870     
23871     /**
23872      * Load data from the requested source (in this case an in-memory
23873      * data object passed to the constructor), read the data object into
23874      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23875      * process that block using the passed callback.
23876      * @param {Object} params This parameter is not used by the MemoryProxy class.
23877      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23878      * object into a block of Roo.data.Records.
23879      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23880      * The function must be passed <ul>
23881      * <li>The Record block object</li>
23882      * <li>The "arg" argument from the load function</li>
23883      * <li>A boolean success indicator</li>
23884      * </ul>
23885      * @param {Object} scope The scope in which to call the callback
23886      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23887      */
23888     load : function(params, reader, callback, scope, arg){
23889         params = params || {};
23890         var result;
23891         try {
23892             result = reader.readRecords(params.data ? params.data :this.data);
23893         }catch(e){
23894             this.fireEvent("loadexception", this, arg, null, e);
23895             callback.call(scope, null, arg, false);
23896             return;
23897         }
23898         callback.call(scope, result, arg, true);
23899     },
23900     
23901     // private
23902     update : function(params, records){
23903         
23904     }
23905 });/*
23906  * Based on:
23907  * Ext JS Library 1.1.1
23908  * Copyright(c) 2006-2007, Ext JS, LLC.
23909  *
23910  * Originally Released Under LGPL - original licence link has changed is not relivant.
23911  *
23912  * Fork - LGPL
23913  * <script type="text/javascript">
23914  */
23915 /**
23916  * @class Roo.data.HttpProxy
23917  * @extends Roo.data.DataProxy
23918  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23919  * configured to reference a certain URL.<br><br>
23920  * <p>
23921  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23922  * from which the running page was served.<br><br>
23923  * <p>
23924  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23925  * <p>
23926  * Be aware that to enable the browser to parse an XML document, the server must set
23927  * the Content-Type header in the HTTP response to "text/xml".
23928  * @constructor
23929  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23930  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23931  * will be used to make the request.
23932  */
23933 Roo.data.HttpProxy = function(conn){
23934     Roo.data.HttpProxy.superclass.constructor.call(this);
23935     // is conn a conn config or a real conn?
23936     this.conn = conn;
23937     this.useAjax = !conn || !conn.events;
23938   
23939 };
23940
23941 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23942     // thse are take from connection...
23943     
23944     /**
23945      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23946      */
23947     /**
23948      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23949      * extra parameters to each request made by this object. (defaults to undefined)
23950      */
23951     /**
23952      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23953      *  to each request made by this object. (defaults to undefined)
23954      */
23955     /**
23956      * @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)
23957      */
23958     /**
23959      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23960      */
23961      /**
23962      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23963      * @type Boolean
23964      */
23965   
23966
23967     /**
23968      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23969      * @type Boolean
23970      */
23971     /**
23972      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23973      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23974      * a finer-grained basis than the DataProxy events.
23975      */
23976     getConnection : function(){
23977         return this.useAjax ? Roo.Ajax : this.conn;
23978     },
23979
23980     /**
23981      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23982      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23983      * process that block using the passed callback.
23984      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23985      * for the request to the remote server.
23986      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23987      * object into a block of Roo.data.Records.
23988      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23989      * The function must be passed <ul>
23990      * <li>The Record block object</li>
23991      * <li>The "arg" argument from the load function</li>
23992      * <li>A boolean success indicator</li>
23993      * </ul>
23994      * @param {Object} scope The scope in which to call the callback
23995      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23996      */
23997     load : function(params, reader, callback, scope, arg){
23998         if(this.fireEvent("beforeload", this, params) !== false){
23999             var  o = {
24000                 params : params || {},
24001                 request: {
24002                     callback : callback,
24003                     scope : scope,
24004                     arg : arg
24005                 },
24006                 reader: reader,
24007                 callback : this.loadResponse,
24008                 scope: this
24009             };
24010             if(this.useAjax){
24011                 Roo.applyIf(o, this.conn);
24012                 if(this.activeRequest){
24013                     Roo.Ajax.abort(this.activeRequest);
24014                 }
24015                 this.activeRequest = Roo.Ajax.request(o);
24016             }else{
24017                 this.conn.request(o);
24018             }
24019         }else{
24020             callback.call(scope||this, null, arg, false);
24021         }
24022     },
24023
24024     // private
24025     loadResponse : function(o, success, response){
24026         delete this.activeRequest;
24027         if(!success){
24028             this.fireEvent("loadexception", this, o, response);
24029             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24030             return;
24031         }
24032         var result;
24033         try {
24034             result = o.reader.read(response);
24035         }catch(e){
24036             this.fireEvent("loadexception", this, o, response, e);
24037             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24038             return;
24039         }
24040         
24041         this.fireEvent("load", this, o, o.request.arg);
24042         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24043     },
24044
24045     // private
24046     update : function(dataSet){
24047
24048     },
24049
24050     // private
24051     updateResponse : function(dataSet){
24052
24053     }
24054 });/*
24055  * Based on:
24056  * Ext JS Library 1.1.1
24057  * Copyright(c) 2006-2007, Ext JS, LLC.
24058  *
24059  * Originally Released Under LGPL - original licence link has changed is not relivant.
24060  *
24061  * Fork - LGPL
24062  * <script type="text/javascript">
24063  */
24064
24065 /**
24066  * @class Roo.data.ScriptTagProxy
24067  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24068  * other than the originating domain of the running page.<br><br>
24069  * <p>
24070  * <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
24071  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24072  * <p>
24073  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24074  * source code that is used as the source inside a &lt;script> tag.<br><br>
24075  * <p>
24076  * In order for the browser to process the returned data, the server must wrap the data object
24077  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24078  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24079  * depending on whether the callback name was passed:
24080  * <p>
24081  * <pre><code>
24082 boolean scriptTag = false;
24083 String cb = request.getParameter("callback");
24084 if (cb != null) {
24085     scriptTag = true;
24086     response.setContentType("text/javascript");
24087 } else {
24088     response.setContentType("application/x-json");
24089 }
24090 Writer out = response.getWriter();
24091 if (scriptTag) {
24092     out.write(cb + "(");
24093 }
24094 out.print(dataBlock.toJsonString());
24095 if (scriptTag) {
24096     out.write(");");
24097 }
24098 </pre></code>
24099  *
24100  * @constructor
24101  * @param {Object} config A configuration object.
24102  */
24103 Roo.data.ScriptTagProxy = function(config){
24104     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24105     Roo.apply(this, config);
24106     this.head = document.getElementsByTagName("head")[0];
24107 };
24108
24109 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24110
24111 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24112     /**
24113      * @cfg {String} url The URL from which to request the data object.
24114      */
24115     /**
24116      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24117      */
24118     timeout : 30000,
24119     /**
24120      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24121      * the server the name of the callback function set up by the load call to process the returned data object.
24122      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24123      * javascript output which calls this named function passing the data object as its only parameter.
24124      */
24125     callbackParam : "callback",
24126     /**
24127      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24128      * name to the request.
24129      */
24130     nocache : true,
24131
24132     /**
24133      * Load data from the configured URL, read the data object into
24134      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24135      * process that block using the passed callback.
24136      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24137      * for the request to the remote server.
24138      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24139      * object into a block of Roo.data.Records.
24140      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24141      * The function must be passed <ul>
24142      * <li>The Record block object</li>
24143      * <li>The "arg" argument from the load function</li>
24144      * <li>A boolean success indicator</li>
24145      * </ul>
24146      * @param {Object} scope The scope in which to call the callback
24147      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24148      */
24149     load : function(params, reader, callback, scope, arg){
24150         if(this.fireEvent("beforeload", this, params) !== false){
24151
24152             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24153
24154             var url = this.url;
24155             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24156             if(this.nocache){
24157                 url += "&_dc=" + (new Date().getTime());
24158             }
24159             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24160             var trans = {
24161                 id : transId,
24162                 cb : "stcCallback"+transId,
24163                 scriptId : "stcScript"+transId,
24164                 params : params,
24165                 arg : arg,
24166                 url : url,
24167                 callback : callback,
24168                 scope : scope,
24169                 reader : reader
24170             };
24171             var conn = this;
24172
24173             window[trans.cb] = function(o){
24174                 conn.handleResponse(o, trans);
24175             };
24176
24177             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24178
24179             if(this.autoAbort !== false){
24180                 this.abort();
24181             }
24182
24183             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24184
24185             var script = document.createElement("script");
24186             script.setAttribute("src", url);
24187             script.setAttribute("type", "text/javascript");
24188             script.setAttribute("id", trans.scriptId);
24189             this.head.appendChild(script);
24190
24191             this.trans = trans;
24192         }else{
24193             callback.call(scope||this, null, arg, false);
24194         }
24195     },
24196
24197     // private
24198     isLoading : function(){
24199         return this.trans ? true : false;
24200     },
24201
24202     /**
24203      * Abort the current server request.
24204      */
24205     abort : function(){
24206         if(this.isLoading()){
24207             this.destroyTrans(this.trans);
24208         }
24209     },
24210
24211     // private
24212     destroyTrans : function(trans, isLoaded){
24213         this.head.removeChild(document.getElementById(trans.scriptId));
24214         clearTimeout(trans.timeoutId);
24215         if(isLoaded){
24216             window[trans.cb] = undefined;
24217             try{
24218                 delete window[trans.cb];
24219             }catch(e){}
24220         }else{
24221             // if hasn't been loaded, wait for load to remove it to prevent script error
24222             window[trans.cb] = function(){
24223                 window[trans.cb] = undefined;
24224                 try{
24225                     delete window[trans.cb];
24226                 }catch(e){}
24227             };
24228         }
24229     },
24230
24231     // private
24232     handleResponse : function(o, trans){
24233         this.trans = false;
24234         this.destroyTrans(trans, true);
24235         var result;
24236         try {
24237             result = trans.reader.readRecords(o);
24238         }catch(e){
24239             this.fireEvent("loadexception", this, o, trans.arg, e);
24240             trans.callback.call(trans.scope||window, null, trans.arg, false);
24241             return;
24242         }
24243         this.fireEvent("load", this, o, trans.arg);
24244         trans.callback.call(trans.scope||window, result, trans.arg, true);
24245     },
24246
24247     // private
24248     handleFailure : function(trans){
24249         this.trans = false;
24250         this.destroyTrans(trans, false);
24251         this.fireEvent("loadexception", this, null, trans.arg);
24252         trans.callback.call(trans.scope||window, null, trans.arg, false);
24253     }
24254 });/*
24255  * Based on:
24256  * Ext JS Library 1.1.1
24257  * Copyright(c) 2006-2007, Ext JS, LLC.
24258  *
24259  * Originally Released Under LGPL - original licence link has changed is not relivant.
24260  *
24261  * Fork - LGPL
24262  * <script type="text/javascript">
24263  */
24264
24265 /**
24266  * @class Roo.data.JsonReader
24267  * @extends Roo.data.DataReader
24268  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24269  * based on mappings in a provided Roo.data.Record constructor.
24270  * 
24271  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24272  * in the reply previously. 
24273  * 
24274  * <p>
24275  * Example code:
24276  * <pre><code>
24277 var RecordDef = Roo.data.Record.create([
24278     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24279     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24280 ]);
24281 var myReader = new Roo.data.JsonReader({
24282     totalProperty: "results",    // The property which contains the total dataset size (optional)
24283     root: "rows",                // The property which contains an Array of row objects
24284     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24285 }, RecordDef);
24286 </code></pre>
24287  * <p>
24288  * This would consume a JSON file like this:
24289  * <pre><code>
24290 { 'results': 2, 'rows': [
24291     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24292     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24293 }
24294 </code></pre>
24295  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24296  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24297  * paged from the remote server.
24298  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24299  * @cfg {String} root name of the property which contains the Array of row objects.
24300  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24301  * @cfg {Array} fields Array of field definition objects
24302  * @constructor
24303  * Create a new JsonReader
24304  * @param {Object} meta Metadata configuration options
24305  * @param {Object} recordType Either an Array of field definition objects,
24306  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24307  */
24308 Roo.data.JsonReader = function(meta, recordType){
24309     
24310     meta = meta || {};
24311     // set some defaults:
24312     Roo.applyIf(meta, {
24313         totalProperty: 'total',
24314         successProperty : 'success',
24315         root : 'data',
24316         id : 'id'
24317     });
24318     
24319     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24320 };
24321 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24322     
24323     readerType : 'Json',
24324     
24325     /**
24326      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24327      * Used by Store query builder to append _requestMeta to params.
24328      * 
24329      */
24330     metaFromRemote : false,
24331     /**
24332      * This method is only used by a DataProxy which has retrieved data from a remote server.
24333      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24334      * @return {Object} data A data block which is used by an Roo.data.Store object as
24335      * a cache of Roo.data.Records.
24336      */
24337     read : function(response){
24338         var json = response.responseText;
24339        
24340         var o = /* eval:var:o */ eval("("+json+")");
24341         if(!o) {
24342             throw {message: "JsonReader.read: Json object not found"};
24343         }
24344         
24345         if(o.metaData){
24346             
24347             delete this.ef;
24348             this.metaFromRemote = true;
24349             this.meta = o.metaData;
24350             this.recordType = Roo.data.Record.create(o.metaData.fields);
24351             this.onMetaChange(this.meta, this.recordType, o);
24352         }
24353         return this.readRecords(o);
24354     },
24355
24356     // private function a store will implement
24357     onMetaChange : function(meta, recordType, o){
24358
24359     },
24360
24361     /**
24362          * @ignore
24363          */
24364     simpleAccess: function(obj, subsc) {
24365         return obj[subsc];
24366     },
24367
24368         /**
24369          * @ignore
24370          */
24371     getJsonAccessor: function(){
24372         var re = /[\[\.]/;
24373         return function(expr) {
24374             try {
24375                 return(re.test(expr))
24376                     ? new Function("obj", "return obj." + expr)
24377                     : function(obj){
24378                         return obj[expr];
24379                     };
24380             } catch(e){}
24381             return Roo.emptyFn;
24382         };
24383     }(),
24384
24385     /**
24386      * Create a data block containing Roo.data.Records from an XML document.
24387      * @param {Object} o An object which contains an Array of row objects in the property specified
24388      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24389      * which contains the total size of the dataset.
24390      * @return {Object} data A data block which is used by an Roo.data.Store object as
24391      * a cache of Roo.data.Records.
24392      */
24393     readRecords : function(o){
24394         /**
24395          * After any data loads, the raw JSON data is available for further custom processing.
24396          * @type Object
24397          */
24398         this.o = o;
24399         var s = this.meta, Record = this.recordType,
24400             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24401
24402 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24403         if (!this.ef) {
24404             if(s.totalProperty) {
24405                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24406                 }
24407                 if(s.successProperty) {
24408                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24409                 }
24410                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24411                 if (s.id) {
24412                         var g = this.getJsonAccessor(s.id);
24413                         this.getId = function(rec) {
24414                                 var r = g(rec);  
24415                                 return (r === undefined || r === "") ? null : r;
24416                         };
24417                 } else {
24418                         this.getId = function(){return null;};
24419                 }
24420             this.ef = [];
24421             for(var jj = 0; jj < fl; jj++){
24422                 f = fi[jj];
24423                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24424                 this.ef[jj] = this.getJsonAccessor(map);
24425             }
24426         }
24427
24428         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24429         if(s.totalProperty){
24430             var vt = parseInt(this.getTotal(o), 10);
24431             if(!isNaN(vt)){
24432                 totalRecords = vt;
24433             }
24434         }
24435         if(s.successProperty){
24436             var vs = this.getSuccess(o);
24437             if(vs === false || vs === 'false'){
24438                 success = false;
24439             }
24440         }
24441         var records = [];
24442         for(var i = 0; i < c; i++){
24443                 var n = root[i];
24444             var values = {};
24445             var id = this.getId(n);
24446             for(var j = 0; j < fl; j++){
24447                 f = fi[j];
24448             var v = this.ef[j](n);
24449             if (!f.convert) {
24450                 Roo.log('missing convert for ' + f.name);
24451                 Roo.log(f);
24452                 continue;
24453             }
24454             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24455             }
24456             var record = new Record(values, id);
24457             record.json = n;
24458             records[i] = record;
24459         }
24460         return {
24461             raw : o,
24462             success : success,
24463             records : records,
24464             totalRecords : totalRecords
24465         };
24466     }
24467 });/*
24468  * Based on:
24469  * Ext JS Library 1.1.1
24470  * Copyright(c) 2006-2007, Ext JS, LLC.
24471  *
24472  * Originally Released Under LGPL - original licence link has changed is not relivant.
24473  *
24474  * Fork - LGPL
24475  * <script type="text/javascript">
24476  */
24477
24478 /**
24479  * @class Roo.data.XmlReader
24480  * @extends Roo.data.DataReader
24481  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24482  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24483  * <p>
24484  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24485  * header in the HTTP response must be set to "text/xml".</em>
24486  * <p>
24487  * Example code:
24488  * <pre><code>
24489 var RecordDef = Roo.data.Record.create([
24490    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24491    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24492 ]);
24493 var myReader = new Roo.data.XmlReader({
24494    totalRecords: "results", // The element which contains the total dataset size (optional)
24495    record: "row",           // The repeated element which contains row information
24496    id: "id"                 // The element within the row that provides an ID for the record (optional)
24497 }, RecordDef);
24498 </code></pre>
24499  * <p>
24500  * This would consume an XML file like this:
24501  * <pre><code>
24502 &lt;?xml?>
24503 &lt;dataset>
24504  &lt;results>2&lt;/results>
24505  &lt;row>
24506    &lt;id>1&lt;/id>
24507    &lt;name>Bill&lt;/name>
24508    &lt;occupation>Gardener&lt;/occupation>
24509  &lt;/row>
24510  &lt;row>
24511    &lt;id>2&lt;/id>
24512    &lt;name>Ben&lt;/name>
24513    &lt;occupation>Horticulturalist&lt;/occupation>
24514  &lt;/row>
24515 &lt;/dataset>
24516 </code></pre>
24517  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24518  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24519  * paged from the remote server.
24520  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24521  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24522  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24523  * a record identifier value.
24524  * @constructor
24525  * Create a new XmlReader
24526  * @param {Object} meta Metadata configuration options
24527  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24528  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24529  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24530  */
24531 Roo.data.XmlReader = function(meta, recordType){
24532     meta = meta || {};
24533     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24534 };
24535 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24536     /**
24537      * This method is only used by a DataProxy which has retrieved data from a remote server.
24538          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24539          * to contain a method called 'responseXML' that returns an XML document object.
24540      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24541      * a cache of Roo.data.Records.
24542      */
24543     read : function(response){
24544         var doc = response.responseXML;
24545         if(!doc) {
24546             throw {message: "XmlReader.read: XML Document not available"};
24547         }
24548         return this.readRecords(doc);
24549     },
24550
24551     /**
24552      * Create a data block containing Roo.data.Records from an XML document.
24553          * @param {Object} doc A parsed XML document.
24554      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24555      * a cache of Roo.data.Records.
24556      */
24557     readRecords : function(doc){
24558         /**
24559          * After any data loads/reads, the raw XML Document is available for further custom processing.
24560          * @type XMLDocument
24561          */
24562         this.xmlData = doc;
24563         var root = doc.documentElement || doc;
24564         var q = Roo.DomQuery;
24565         var recordType = this.recordType, fields = recordType.prototype.fields;
24566         var sid = this.meta.id;
24567         var totalRecords = 0, success = true;
24568         if(this.meta.totalRecords){
24569             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24570         }
24571         
24572         if(this.meta.success){
24573             var sv = q.selectValue(this.meta.success, root, true);
24574             success = sv !== false && sv !== 'false';
24575         }
24576         var records = [];
24577         var ns = q.select(this.meta.record, root);
24578         for(var i = 0, len = ns.length; i < len; i++) {
24579                 var n = ns[i];
24580                 var values = {};
24581                 var id = sid ? q.selectValue(sid, n) : undefined;
24582                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24583                     var f = fields.items[j];
24584                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24585                     v = f.convert(v);
24586                     values[f.name] = v;
24587                 }
24588                 var record = new recordType(values, id);
24589                 record.node = n;
24590                 records[records.length] = record;
24591             }
24592
24593             return {
24594                 success : success,
24595                 records : records,
24596                 totalRecords : totalRecords || records.length
24597             };
24598     }
24599 });/*
24600  * Based on:
24601  * Ext JS Library 1.1.1
24602  * Copyright(c) 2006-2007, Ext JS, LLC.
24603  *
24604  * Originally Released Under LGPL - original licence link has changed is not relivant.
24605  *
24606  * Fork - LGPL
24607  * <script type="text/javascript">
24608  */
24609
24610 /**
24611  * @class Roo.data.ArrayReader
24612  * @extends Roo.data.DataReader
24613  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24614  * Each element of that Array represents a row of data fields. The
24615  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24616  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24617  * <p>
24618  * Example code:.
24619  * <pre><code>
24620 var RecordDef = Roo.data.Record.create([
24621     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24622     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24623 ]);
24624 var myReader = new Roo.data.ArrayReader({
24625     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24626 }, RecordDef);
24627 </code></pre>
24628  * <p>
24629  * This would consume an Array like this:
24630  * <pre><code>
24631 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24632   </code></pre>
24633  
24634  * @constructor
24635  * Create a new JsonReader
24636  * @param {Object} meta Metadata configuration options.
24637  * @param {Object|Array} recordType Either an Array of field definition objects
24638  * 
24639  * @cfg {Array} fields Array of field definition objects
24640  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24641  * as specified to {@link Roo.data.Record#create},
24642  * or an {@link Roo.data.Record} object
24643  *
24644  * 
24645  * created using {@link Roo.data.Record#create}.
24646  */
24647 Roo.data.ArrayReader = function(meta, recordType)
24648 {    
24649     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24650 };
24651
24652 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24653     /**
24654      * Create a data block containing Roo.data.Records from an XML document.
24655      * @param {Object} o An Array of row objects which represents the dataset.
24656      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
24657      * a cache of Roo.data.Records.
24658      */
24659     readRecords : function(o)
24660     {
24661         var sid = this.meta ? this.meta.id : null;
24662         var recordType = this.recordType, fields = recordType.prototype.fields;
24663         var records = [];
24664         var root = o;
24665         for(var i = 0; i < root.length; i++){
24666                 var n = root[i];
24667             var values = {};
24668             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24669             for(var j = 0, jlen = fields.length; j < jlen; j++){
24670                 var f = fields.items[j];
24671                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24672                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24673                 v = f.convert(v);
24674                 values[f.name] = v;
24675             }
24676             var record = new recordType(values, id);
24677             record.json = n;
24678             records[records.length] = record;
24679         }
24680         return {
24681             records : records,
24682             totalRecords : records.length
24683         };
24684     }
24685 });/*
24686  * Based on:
24687  * Ext JS Library 1.1.1
24688  * Copyright(c) 2006-2007, Ext JS, LLC.
24689  *
24690  * Originally Released Under LGPL - original licence link has changed is not relivant.
24691  *
24692  * Fork - LGPL
24693  * <script type="text/javascript">
24694  */
24695
24696
24697 /**
24698  * @class Roo.data.Tree
24699  * @extends Roo.util.Observable
24700  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24701  * in the tree have most standard DOM functionality.
24702  * @constructor
24703  * @param {Node} root (optional) The root node
24704  */
24705 Roo.data.Tree = function(root){
24706    this.nodeHash = {};
24707    /**
24708     * The root node for this tree
24709     * @type Node
24710     */
24711    this.root = null;
24712    if(root){
24713        this.setRootNode(root);
24714    }
24715    this.addEvents({
24716        /**
24717         * @event append
24718         * Fires when a new child node is appended to a node in this tree.
24719         * @param {Tree} tree The owner tree
24720         * @param {Node} parent The parent node
24721         * @param {Node} node The newly appended node
24722         * @param {Number} index The index of the newly appended node
24723         */
24724        "append" : true,
24725        /**
24726         * @event remove
24727         * Fires when a child node is removed from a node in this tree.
24728         * @param {Tree} tree The owner tree
24729         * @param {Node} parent The parent node
24730         * @param {Node} node The child node removed
24731         */
24732        "remove" : true,
24733        /**
24734         * @event move
24735         * Fires when a node is moved to a new location in the tree
24736         * @param {Tree} tree The owner tree
24737         * @param {Node} node The node moved
24738         * @param {Node} oldParent The old parent of this node
24739         * @param {Node} newParent The new parent of this node
24740         * @param {Number} index The index it was moved to
24741         */
24742        "move" : true,
24743        /**
24744         * @event insert
24745         * Fires when a new child node is inserted in a node in this tree.
24746         * @param {Tree} tree The owner tree
24747         * @param {Node} parent The parent node
24748         * @param {Node} node The child node inserted
24749         * @param {Node} refNode The child node the node was inserted before
24750         */
24751        "insert" : true,
24752        /**
24753         * @event beforeappend
24754         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24755         * @param {Tree} tree The owner tree
24756         * @param {Node} parent The parent node
24757         * @param {Node} node The child node to be appended
24758         */
24759        "beforeappend" : true,
24760        /**
24761         * @event beforeremove
24762         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24763         * @param {Tree} tree The owner tree
24764         * @param {Node} parent The parent node
24765         * @param {Node} node The child node to be removed
24766         */
24767        "beforeremove" : true,
24768        /**
24769         * @event beforemove
24770         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24771         * @param {Tree} tree The owner tree
24772         * @param {Node} node The node being moved
24773         * @param {Node} oldParent The parent of the node
24774         * @param {Node} newParent The new parent the node is moving to
24775         * @param {Number} index The index it is being moved to
24776         */
24777        "beforemove" : true,
24778        /**
24779         * @event beforeinsert
24780         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24781         * @param {Tree} tree The owner tree
24782         * @param {Node} parent The parent node
24783         * @param {Node} node The child node to be inserted
24784         * @param {Node} refNode The child node the node is being inserted before
24785         */
24786        "beforeinsert" : true
24787    });
24788
24789     Roo.data.Tree.superclass.constructor.call(this);
24790 };
24791
24792 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24793     pathSeparator: "/",
24794
24795     proxyNodeEvent : function(){
24796         return this.fireEvent.apply(this, arguments);
24797     },
24798
24799     /**
24800      * Returns the root node for this tree.
24801      * @return {Node}
24802      */
24803     getRootNode : function(){
24804         return this.root;
24805     },
24806
24807     /**
24808      * Sets the root node for this tree.
24809      * @param {Node} node
24810      * @return {Node}
24811      */
24812     setRootNode : function(node){
24813         this.root = node;
24814         node.ownerTree = this;
24815         node.isRoot = true;
24816         this.registerNode(node);
24817         return node;
24818     },
24819
24820     /**
24821      * Gets a node in this tree by its id.
24822      * @param {String} id
24823      * @return {Node}
24824      */
24825     getNodeById : function(id){
24826         return this.nodeHash[id];
24827     },
24828
24829     registerNode : function(node){
24830         this.nodeHash[node.id] = node;
24831     },
24832
24833     unregisterNode : function(node){
24834         delete this.nodeHash[node.id];
24835     },
24836
24837     toString : function(){
24838         return "[Tree"+(this.id?" "+this.id:"")+"]";
24839     }
24840 });
24841
24842 /**
24843  * @class Roo.data.Node
24844  * @extends Roo.util.Observable
24845  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24846  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24847  * @constructor
24848  * @param {Object} attributes The attributes/config for the node
24849  */
24850 Roo.data.Node = function(attributes){
24851     /**
24852      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24853      * @type {Object}
24854      */
24855     this.attributes = attributes || {};
24856     this.leaf = this.attributes.leaf;
24857     /**
24858      * The node id. @type String
24859      */
24860     this.id = this.attributes.id;
24861     if(!this.id){
24862         this.id = Roo.id(null, "ynode-");
24863         this.attributes.id = this.id;
24864     }
24865      
24866     
24867     /**
24868      * All child nodes of this node. @type Array
24869      */
24870     this.childNodes = [];
24871     if(!this.childNodes.indexOf){ // indexOf is a must
24872         this.childNodes.indexOf = function(o){
24873             for(var i = 0, len = this.length; i < len; i++){
24874                 if(this[i] == o) {
24875                     return i;
24876                 }
24877             }
24878             return -1;
24879         };
24880     }
24881     /**
24882      * The parent node for this node. @type Node
24883      */
24884     this.parentNode = null;
24885     /**
24886      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24887      */
24888     this.firstChild = null;
24889     /**
24890      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24891      */
24892     this.lastChild = null;
24893     /**
24894      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24895      */
24896     this.previousSibling = null;
24897     /**
24898      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24899      */
24900     this.nextSibling = null;
24901
24902     this.addEvents({
24903        /**
24904         * @event append
24905         * Fires when a new child node is appended
24906         * @param {Tree} tree The owner tree
24907         * @param {Node} this This node
24908         * @param {Node} node The newly appended node
24909         * @param {Number} index The index of the newly appended node
24910         */
24911        "append" : true,
24912        /**
24913         * @event remove
24914         * Fires when a child node is removed
24915         * @param {Tree} tree The owner tree
24916         * @param {Node} this This node
24917         * @param {Node} node The removed node
24918         */
24919        "remove" : true,
24920        /**
24921         * @event move
24922         * Fires when this node is moved to a new location in the tree
24923         * @param {Tree} tree The owner tree
24924         * @param {Node} this This node
24925         * @param {Node} oldParent The old parent of this node
24926         * @param {Node} newParent The new parent of this node
24927         * @param {Number} index The index it was moved to
24928         */
24929        "move" : true,
24930        /**
24931         * @event insert
24932         * Fires when a new child node is inserted.
24933         * @param {Tree} tree The owner tree
24934         * @param {Node} this This node
24935         * @param {Node} node The child node inserted
24936         * @param {Node} refNode The child node the node was inserted before
24937         */
24938        "insert" : true,
24939        /**
24940         * @event beforeappend
24941         * Fires before a new child is appended, return false to cancel the append.
24942         * @param {Tree} tree The owner tree
24943         * @param {Node} this This node
24944         * @param {Node} node The child node to be appended
24945         */
24946        "beforeappend" : true,
24947        /**
24948         * @event beforeremove
24949         * Fires before a child is removed, return false to cancel the remove.
24950         * @param {Tree} tree The owner tree
24951         * @param {Node} this This node
24952         * @param {Node} node The child node to be removed
24953         */
24954        "beforeremove" : true,
24955        /**
24956         * @event beforemove
24957         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24958         * @param {Tree} tree The owner tree
24959         * @param {Node} this This node
24960         * @param {Node} oldParent The parent of this node
24961         * @param {Node} newParent The new parent this node is moving to
24962         * @param {Number} index The index it is being moved to
24963         */
24964        "beforemove" : true,
24965        /**
24966         * @event beforeinsert
24967         * Fires before a new child is inserted, return false to cancel the insert.
24968         * @param {Tree} tree The owner tree
24969         * @param {Node} this This node
24970         * @param {Node} node The child node to be inserted
24971         * @param {Node} refNode The child node the node is being inserted before
24972         */
24973        "beforeinsert" : true
24974    });
24975     this.listeners = this.attributes.listeners;
24976     Roo.data.Node.superclass.constructor.call(this);
24977 };
24978
24979 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24980     fireEvent : function(evtName){
24981         // first do standard event for this node
24982         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24983             return false;
24984         }
24985         // then bubble it up to the tree if the event wasn't cancelled
24986         var ot = this.getOwnerTree();
24987         if(ot){
24988             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24989                 return false;
24990             }
24991         }
24992         return true;
24993     },
24994
24995     /**
24996      * Returns true if this node is a leaf
24997      * @return {Boolean}
24998      */
24999     isLeaf : function(){
25000         return this.leaf === true;
25001     },
25002
25003     // private
25004     setFirstChild : function(node){
25005         this.firstChild = node;
25006     },
25007
25008     //private
25009     setLastChild : function(node){
25010         this.lastChild = node;
25011     },
25012
25013
25014     /**
25015      * Returns true if this node is the last child of its parent
25016      * @return {Boolean}
25017      */
25018     isLast : function(){
25019        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25020     },
25021
25022     /**
25023      * Returns true if this node is the first child of its parent
25024      * @return {Boolean}
25025      */
25026     isFirst : function(){
25027        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25028     },
25029
25030     hasChildNodes : function(){
25031         return !this.isLeaf() && this.childNodes.length > 0;
25032     },
25033
25034     /**
25035      * Insert node(s) as the last child node of this node.
25036      * @param {Node/Array} node The node or Array of nodes to append
25037      * @return {Node} The appended node if single append, or null if an array was passed
25038      */
25039     appendChild : function(node){
25040         var multi = false;
25041         if(node instanceof Array){
25042             multi = node;
25043         }else if(arguments.length > 1){
25044             multi = arguments;
25045         }
25046         
25047         // if passed an array or multiple args do them one by one
25048         if(multi){
25049             for(var i = 0, len = multi.length; i < len; i++) {
25050                 this.appendChild(multi[i]);
25051             }
25052         }else{
25053             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25054                 return false;
25055             }
25056             var index = this.childNodes.length;
25057             var oldParent = node.parentNode;
25058             // it's a move, make sure we move it cleanly
25059             if(oldParent){
25060                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25061                     return false;
25062                 }
25063                 oldParent.removeChild(node);
25064             }
25065             
25066             index = this.childNodes.length;
25067             if(index == 0){
25068                 this.setFirstChild(node);
25069             }
25070             this.childNodes.push(node);
25071             node.parentNode = this;
25072             var ps = this.childNodes[index-1];
25073             if(ps){
25074                 node.previousSibling = ps;
25075                 ps.nextSibling = node;
25076             }else{
25077                 node.previousSibling = null;
25078             }
25079             node.nextSibling = null;
25080             this.setLastChild(node);
25081             node.setOwnerTree(this.getOwnerTree());
25082             this.fireEvent("append", this.ownerTree, this, node, index);
25083             if(this.ownerTree) {
25084                 this.ownerTree.fireEvent("appendnode", this, node, index);
25085             }
25086             if(oldParent){
25087                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25088             }
25089             return node;
25090         }
25091     },
25092
25093     /**
25094      * Removes a child node from this node.
25095      * @param {Node} node The node to remove
25096      * @return {Node} The removed node
25097      */
25098     removeChild : function(node){
25099         var index = this.childNodes.indexOf(node);
25100         if(index == -1){
25101             return false;
25102         }
25103         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25104             return false;
25105         }
25106
25107         // remove it from childNodes collection
25108         this.childNodes.splice(index, 1);
25109
25110         // update siblings
25111         if(node.previousSibling){
25112             node.previousSibling.nextSibling = node.nextSibling;
25113         }
25114         if(node.nextSibling){
25115             node.nextSibling.previousSibling = node.previousSibling;
25116         }
25117
25118         // update child refs
25119         if(this.firstChild == node){
25120             this.setFirstChild(node.nextSibling);
25121         }
25122         if(this.lastChild == node){
25123             this.setLastChild(node.previousSibling);
25124         }
25125
25126         node.setOwnerTree(null);
25127         // clear any references from the node
25128         node.parentNode = null;
25129         node.previousSibling = null;
25130         node.nextSibling = null;
25131         this.fireEvent("remove", this.ownerTree, this, node);
25132         return node;
25133     },
25134
25135     /**
25136      * Inserts the first node before the second node in this nodes childNodes collection.
25137      * @param {Node} node The node to insert
25138      * @param {Node} refNode The node to insert before (if null the node is appended)
25139      * @return {Node} The inserted node
25140      */
25141     insertBefore : function(node, refNode){
25142         if(!refNode){ // like standard Dom, refNode can be null for append
25143             return this.appendChild(node);
25144         }
25145         // nothing to do
25146         if(node == refNode){
25147             return false;
25148         }
25149
25150         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25151             return false;
25152         }
25153         var index = this.childNodes.indexOf(refNode);
25154         var oldParent = node.parentNode;
25155         var refIndex = index;
25156
25157         // when moving internally, indexes will change after remove
25158         if(oldParent == this && this.childNodes.indexOf(node) < index){
25159             refIndex--;
25160         }
25161
25162         // it's a move, make sure we move it cleanly
25163         if(oldParent){
25164             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25165                 return false;
25166             }
25167             oldParent.removeChild(node);
25168         }
25169         if(refIndex == 0){
25170             this.setFirstChild(node);
25171         }
25172         this.childNodes.splice(refIndex, 0, node);
25173         node.parentNode = this;
25174         var ps = this.childNodes[refIndex-1];
25175         if(ps){
25176             node.previousSibling = ps;
25177             ps.nextSibling = node;
25178         }else{
25179             node.previousSibling = null;
25180         }
25181         node.nextSibling = refNode;
25182         refNode.previousSibling = node;
25183         node.setOwnerTree(this.getOwnerTree());
25184         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25185         if(oldParent){
25186             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25187         }
25188         return node;
25189     },
25190
25191     /**
25192      * Returns the child node at the specified index.
25193      * @param {Number} index
25194      * @return {Node}
25195      */
25196     item : function(index){
25197         return this.childNodes[index];
25198     },
25199
25200     /**
25201      * Replaces one child node in this node with another.
25202      * @param {Node} newChild The replacement node
25203      * @param {Node} oldChild The node to replace
25204      * @return {Node} The replaced node
25205      */
25206     replaceChild : function(newChild, oldChild){
25207         this.insertBefore(newChild, oldChild);
25208         this.removeChild(oldChild);
25209         return oldChild;
25210     },
25211
25212     /**
25213      * Returns the index of a child node
25214      * @param {Node} node
25215      * @return {Number} The index of the node or -1 if it was not found
25216      */
25217     indexOf : function(child){
25218         return this.childNodes.indexOf(child);
25219     },
25220
25221     /**
25222      * Returns the tree this node is in.
25223      * @return {Tree}
25224      */
25225     getOwnerTree : function(){
25226         // if it doesn't have one, look for one
25227         if(!this.ownerTree){
25228             var p = this;
25229             while(p){
25230                 if(p.ownerTree){
25231                     this.ownerTree = p.ownerTree;
25232                     break;
25233                 }
25234                 p = p.parentNode;
25235             }
25236         }
25237         return this.ownerTree;
25238     },
25239
25240     /**
25241      * Returns depth of this node (the root node has a depth of 0)
25242      * @return {Number}
25243      */
25244     getDepth : function(){
25245         var depth = 0;
25246         var p = this;
25247         while(p.parentNode){
25248             ++depth;
25249             p = p.parentNode;
25250         }
25251         return depth;
25252     },
25253
25254     // private
25255     setOwnerTree : function(tree){
25256         // if it's move, we need to update everyone
25257         if(tree != this.ownerTree){
25258             if(this.ownerTree){
25259                 this.ownerTree.unregisterNode(this);
25260             }
25261             this.ownerTree = tree;
25262             var cs = this.childNodes;
25263             for(var i = 0, len = cs.length; i < len; i++) {
25264                 cs[i].setOwnerTree(tree);
25265             }
25266             if(tree){
25267                 tree.registerNode(this);
25268             }
25269         }
25270     },
25271
25272     /**
25273      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25274      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25275      * @return {String} The path
25276      */
25277     getPath : function(attr){
25278         attr = attr || "id";
25279         var p = this.parentNode;
25280         var b = [this.attributes[attr]];
25281         while(p){
25282             b.unshift(p.attributes[attr]);
25283             p = p.parentNode;
25284         }
25285         var sep = this.getOwnerTree().pathSeparator;
25286         return sep + b.join(sep);
25287     },
25288
25289     /**
25290      * Bubbles up the tree from 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 bubble is stopped.
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     bubble : function(fn, scope, args){
25299         var p = this;
25300         while(p){
25301             if(fn.call(scope || p, args || p) === false){
25302                 break;
25303             }
25304             p = p.parentNode;
25305         }
25306     },
25307
25308     /**
25309      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25310      * function call will be the scope provided or the current node. The arguments to the function
25311      * will be the args provided or the current node. If the function returns false at any point,
25312      * the cascade is stopped on that branch.
25313      * @param {Function} fn The function to call
25314      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25315      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25316      */
25317     cascade : function(fn, scope, args){
25318         if(fn.call(scope || this, args || this) !== false){
25319             var cs = this.childNodes;
25320             for(var i = 0, len = cs.length; i < len; i++) {
25321                 cs[i].cascade(fn, scope, args);
25322             }
25323         }
25324     },
25325
25326     /**
25327      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25328      * function call will be the scope provided or the current node. The arguments to the function
25329      * will be the args provided or the current node. If the function returns false at any point,
25330      * the iteration stops.
25331      * @param {Function} fn The function to call
25332      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25333      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25334      */
25335     eachChild : function(fn, scope, args){
25336         var cs = this.childNodes;
25337         for(var i = 0, len = cs.length; i < len; i++) {
25338                 if(fn.call(scope || this, args || cs[i]) === false){
25339                     break;
25340                 }
25341         }
25342     },
25343
25344     /**
25345      * Finds the first child that has the attribute with the specified value.
25346      * @param {String} attribute The attribute name
25347      * @param {Mixed} value The value to search for
25348      * @return {Node} The found child or null if none was found
25349      */
25350     findChild : function(attribute, value){
25351         var cs = this.childNodes;
25352         for(var i = 0, len = cs.length; i < len; i++) {
25353                 if(cs[i].attributes[attribute] == value){
25354                     return cs[i];
25355                 }
25356         }
25357         return null;
25358     },
25359
25360     /**
25361      * Finds the first child by a custom function. The child matches if the function passed
25362      * returns true.
25363      * @param {Function} fn
25364      * @param {Object} scope (optional)
25365      * @return {Node} The found child or null if none was found
25366      */
25367     findChildBy : function(fn, scope){
25368         var cs = this.childNodes;
25369         for(var i = 0, len = cs.length; i < len; i++) {
25370                 if(fn.call(scope||cs[i], cs[i]) === true){
25371                     return cs[i];
25372                 }
25373         }
25374         return null;
25375     },
25376
25377     /**
25378      * Sorts this nodes children using the supplied sort function
25379      * @param {Function} fn
25380      * @param {Object} scope (optional)
25381      */
25382     sort : function(fn, scope){
25383         var cs = this.childNodes;
25384         var len = cs.length;
25385         if(len > 0){
25386             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25387             cs.sort(sortFn);
25388             for(var i = 0; i < len; i++){
25389                 var n = cs[i];
25390                 n.previousSibling = cs[i-1];
25391                 n.nextSibling = cs[i+1];
25392                 if(i == 0){
25393                     this.setFirstChild(n);
25394                 }
25395                 if(i == len-1){
25396                     this.setLastChild(n);
25397                 }
25398             }
25399         }
25400     },
25401
25402     /**
25403      * Returns true if this node is an ancestor (at any point) of the passed node.
25404      * @param {Node} node
25405      * @return {Boolean}
25406      */
25407     contains : function(node){
25408         return node.isAncestor(this);
25409     },
25410
25411     /**
25412      * Returns true if the passed node is an ancestor (at any point) of this node.
25413      * @param {Node} node
25414      * @return {Boolean}
25415      */
25416     isAncestor : function(node){
25417         var p = this.parentNode;
25418         while(p){
25419             if(p == node){
25420                 return true;
25421             }
25422             p = p.parentNode;
25423         }
25424         return false;
25425     },
25426
25427     toString : function(){
25428         return "[Node"+(this.id?" "+this.id:"")+"]";
25429     }
25430 });/*
25431  * Based on:
25432  * Ext JS Library 1.1.1
25433  * Copyright(c) 2006-2007, Ext JS, LLC.
25434  *
25435  * Originally Released Under LGPL - original licence link has changed is not relivant.
25436  *
25437  * Fork - LGPL
25438  * <script type="text/javascript">
25439  */
25440  (function(){ 
25441 /**
25442  * @class Roo.Layer
25443  * @extends Roo.Element
25444  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25445  * automatic maintaining of shadow/shim positions.
25446  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25447  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25448  * you can pass a string with a CSS class name. False turns off the shadow.
25449  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25450  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25451  * @cfg {String} cls CSS class to add to the element
25452  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25453  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25454  * @constructor
25455  * @param {Object} config An object with config options.
25456  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25457  */
25458
25459 Roo.Layer = function(config, existingEl){
25460     config = config || {};
25461     var dh = Roo.DomHelper;
25462     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25463     if(existingEl){
25464         this.dom = Roo.getDom(existingEl);
25465     }
25466     if(!this.dom){
25467         var o = config.dh || {tag: "div", cls: "x-layer"};
25468         this.dom = dh.append(pel, o);
25469     }
25470     if(config.cls){
25471         this.addClass(config.cls);
25472     }
25473     this.constrain = config.constrain !== false;
25474     this.visibilityMode = Roo.Element.VISIBILITY;
25475     if(config.id){
25476         this.id = this.dom.id = config.id;
25477     }else{
25478         this.id = Roo.id(this.dom);
25479     }
25480     this.zindex = config.zindex || this.getZIndex();
25481     this.position("absolute", this.zindex);
25482     if(config.shadow){
25483         this.shadowOffset = config.shadowOffset || 4;
25484         this.shadow = new Roo.Shadow({
25485             offset : this.shadowOffset,
25486             mode : config.shadow
25487         });
25488     }else{
25489         this.shadowOffset = 0;
25490     }
25491     this.useShim = config.shim !== false && Roo.useShims;
25492     this.useDisplay = config.useDisplay;
25493     this.hide();
25494 };
25495
25496 var supr = Roo.Element.prototype;
25497
25498 // shims are shared among layer to keep from having 100 iframes
25499 var shims = [];
25500
25501 Roo.extend(Roo.Layer, Roo.Element, {
25502
25503     getZIndex : function(){
25504         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25505     },
25506
25507     getShim : function(){
25508         if(!this.useShim){
25509             return null;
25510         }
25511         if(this.shim){
25512             return this.shim;
25513         }
25514         var shim = shims.shift();
25515         if(!shim){
25516             shim = this.createShim();
25517             shim.enableDisplayMode('block');
25518             shim.dom.style.display = 'none';
25519             shim.dom.style.visibility = 'visible';
25520         }
25521         var pn = this.dom.parentNode;
25522         if(shim.dom.parentNode != pn){
25523             pn.insertBefore(shim.dom, this.dom);
25524         }
25525         shim.setStyle('z-index', this.getZIndex()-2);
25526         this.shim = shim;
25527         return shim;
25528     },
25529
25530     hideShim : function(){
25531         if(this.shim){
25532             this.shim.setDisplayed(false);
25533             shims.push(this.shim);
25534             delete this.shim;
25535         }
25536     },
25537
25538     disableShadow : function(){
25539         if(this.shadow){
25540             this.shadowDisabled = true;
25541             this.shadow.hide();
25542             this.lastShadowOffset = this.shadowOffset;
25543             this.shadowOffset = 0;
25544         }
25545     },
25546
25547     enableShadow : function(show){
25548         if(this.shadow){
25549             this.shadowDisabled = false;
25550             this.shadowOffset = this.lastShadowOffset;
25551             delete this.lastShadowOffset;
25552             if(show){
25553                 this.sync(true);
25554             }
25555         }
25556     },
25557
25558     // private
25559     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25560     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25561     sync : function(doShow){
25562         var sw = this.shadow;
25563         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25564             var sh = this.getShim();
25565
25566             var w = this.getWidth(),
25567                 h = this.getHeight();
25568
25569             var l = this.getLeft(true),
25570                 t = this.getTop(true);
25571
25572             if(sw && !this.shadowDisabled){
25573                 if(doShow && !sw.isVisible()){
25574                     sw.show(this);
25575                 }else{
25576                     sw.realign(l, t, w, h);
25577                 }
25578                 if(sh){
25579                     if(doShow){
25580                        sh.show();
25581                     }
25582                     // fit the shim behind the shadow, so it is shimmed too
25583                     var a = sw.adjusts, s = sh.dom.style;
25584                     s.left = (Math.min(l, l+a.l))+"px";
25585                     s.top = (Math.min(t, t+a.t))+"px";
25586                     s.width = (w+a.w)+"px";
25587                     s.height = (h+a.h)+"px";
25588                 }
25589             }else if(sh){
25590                 if(doShow){
25591                    sh.show();
25592                 }
25593                 sh.setSize(w, h);
25594                 sh.setLeftTop(l, t);
25595             }
25596             
25597         }
25598     },
25599
25600     // private
25601     destroy : function(){
25602         this.hideShim();
25603         if(this.shadow){
25604             this.shadow.hide();
25605         }
25606         this.removeAllListeners();
25607         var pn = this.dom.parentNode;
25608         if(pn){
25609             pn.removeChild(this.dom);
25610         }
25611         Roo.Element.uncache(this.id);
25612     },
25613
25614     remove : function(){
25615         this.destroy();
25616     },
25617
25618     // private
25619     beginUpdate : function(){
25620         this.updating = true;
25621     },
25622
25623     // private
25624     endUpdate : function(){
25625         this.updating = false;
25626         this.sync(true);
25627     },
25628
25629     // private
25630     hideUnders : function(negOffset){
25631         if(this.shadow){
25632             this.shadow.hide();
25633         }
25634         this.hideShim();
25635     },
25636
25637     // private
25638     constrainXY : function(){
25639         if(this.constrain){
25640             var vw = Roo.lib.Dom.getViewWidth(),
25641                 vh = Roo.lib.Dom.getViewHeight();
25642             var s = Roo.get(document).getScroll();
25643
25644             var xy = this.getXY();
25645             var x = xy[0], y = xy[1];   
25646             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25647             // only move it if it needs it
25648             var moved = false;
25649             // first validate right/bottom
25650             if((x + w) > vw+s.left){
25651                 x = vw - w - this.shadowOffset;
25652                 moved = true;
25653             }
25654             if((y + h) > vh+s.top){
25655                 y = vh - h - this.shadowOffset;
25656                 moved = true;
25657             }
25658             // then make sure top/left isn't negative
25659             if(x < s.left){
25660                 x = s.left;
25661                 moved = true;
25662             }
25663             if(y < s.top){
25664                 y = s.top;
25665                 moved = true;
25666             }
25667             if(moved){
25668                 if(this.avoidY){
25669                     var ay = this.avoidY;
25670                     if(y <= ay && (y+h) >= ay){
25671                         y = ay-h-5;   
25672                     }
25673                 }
25674                 xy = [x, y];
25675                 this.storeXY(xy);
25676                 supr.setXY.call(this, xy);
25677                 this.sync();
25678             }
25679         }
25680     },
25681
25682     isVisible : function(){
25683         return this.visible;    
25684     },
25685
25686     // private
25687     showAction : function(){
25688         this.visible = true; // track visibility to prevent getStyle calls
25689         if(this.useDisplay === true){
25690             this.setDisplayed("");
25691         }else if(this.lastXY){
25692             supr.setXY.call(this, this.lastXY);
25693         }else if(this.lastLT){
25694             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25695         }
25696     },
25697
25698     // private
25699     hideAction : function(){
25700         this.visible = false;
25701         if(this.useDisplay === true){
25702             this.setDisplayed(false);
25703         }else{
25704             this.setLeftTop(-10000,-10000);
25705         }
25706     },
25707
25708     // overridden Element method
25709     setVisible : function(v, a, d, c, e){
25710         if(v){
25711             this.showAction();
25712         }
25713         if(a && v){
25714             var cb = function(){
25715                 this.sync(true);
25716                 if(c){
25717                     c();
25718                 }
25719             }.createDelegate(this);
25720             supr.setVisible.call(this, true, true, d, cb, e);
25721         }else{
25722             if(!v){
25723                 this.hideUnders(true);
25724             }
25725             var cb = c;
25726             if(a){
25727                 cb = function(){
25728                     this.hideAction();
25729                     if(c){
25730                         c();
25731                     }
25732                 }.createDelegate(this);
25733             }
25734             supr.setVisible.call(this, v, a, d, cb, e);
25735             if(v){
25736                 this.sync(true);
25737             }else if(!a){
25738                 this.hideAction();
25739             }
25740         }
25741     },
25742
25743     storeXY : function(xy){
25744         delete this.lastLT;
25745         this.lastXY = xy;
25746     },
25747
25748     storeLeftTop : function(left, top){
25749         delete this.lastXY;
25750         this.lastLT = [left, top];
25751     },
25752
25753     // private
25754     beforeFx : function(){
25755         this.beforeAction();
25756         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25757     },
25758
25759     // private
25760     afterFx : function(){
25761         Roo.Layer.superclass.afterFx.apply(this, arguments);
25762         this.sync(this.isVisible());
25763     },
25764
25765     // private
25766     beforeAction : function(){
25767         if(!this.updating && this.shadow){
25768             this.shadow.hide();
25769         }
25770     },
25771
25772     // overridden Element method
25773     setLeft : function(left){
25774         this.storeLeftTop(left, this.getTop(true));
25775         supr.setLeft.apply(this, arguments);
25776         this.sync();
25777     },
25778
25779     setTop : function(top){
25780         this.storeLeftTop(this.getLeft(true), top);
25781         supr.setTop.apply(this, arguments);
25782         this.sync();
25783     },
25784
25785     setLeftTop : function(left, top){
25786         this.storeLeftTop(left, top);
25787         supr.setLeftTop.apply(this, arguments);
25788         this.sync();
25789     },
25790
25791     setXY : function(xy, a, d, c, e){
25792         this.fixDisplay();
25793         this.beforeAction();
25794         this.storeXY(xy);
25795         var cb = this.createCB(c);
25796         supr.setXY.call(this, xy, a, d, cb, e);
25797         if(!a){
25798             cb();
25799         }
25800     },
25801
25802     // private
25803     createCB : function(c){
25804         var el = this;
25805         return function(){
25806             el.constrainXY();
25807             el.sync(true);
25808             if(c){
25809                 c();
25810             }
25811         };
25812     },
25813
25814     // overridden Element method
25815     setX : function(x, a, d, c, e){
25816         this.setXY([x, this.getY()], a, d, c, e);
25817     },
25818
25819     // overridden Element method
25820     setY : function(y, a, d, c, e){
25821         this.setXY([this.getX(), y], a, d, c, e);
25822     },
25823
25824     // overridden Element method
25825     setSize : function(w, h, a, d, c, e){
25826         this.beforeAction();
25827         var cb = this.createCB(c);
25828         supr.setSize.call(this, w, h, a, d, cb, e);
25829         if(!a){
25830             cb();
25831         }
25832     },
25833
25834     // overridden Element method
25835     setWidth : function(w, a, d, c, e){
25836         this.beforeAction();
25837         var cb = this.createCB(c);
25838         supr.setWidth.call(this, w, a, d, cb, e);
25839         if(!a){
25840             cb();
25841         }
25842     },
25843
25844     // overridden Element method
25845     setHeight : function(h, a, d, c, e){
25846         this.beforeAction();
25847         var cb = this.createCB(c);
25848         supr.setHeight.call(this, h, a, d, cb, e);
25849         if(!a){
25850             cb();
25851         }
25852     },
25853
25854     // overridden Element method
25855     setBounds : function(x, y, w, h, a, d, c, e){
25856         this.beforeAction();
25857         var cb = this.createCB(c);
25858         if(!a){
25859             this.storeXY([x, y]);
25860             supr.setXY.call(this, [x, y]);
25861             supr.setSize.call(this, w, h, a, d, cb, e);
25862             cb();
25863         }else{
25864             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25865         }
25866         return this;
25867     },
25868     
25869     /**
25870      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25871      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25872      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25873      * @param {Number} zindex The new z-index to set
25874      * @return {this} The Layer
25875      */
25876     setZIndex : function(zindex){
25877         this.zindex = zindex;
25878         this.setStyle("z-index", zindex + 2);
25879         if(this.shadow){
25880             this.shadow.setZIndex(zindex + 1);
25881         }
25882         if(this.shim){
25883             this.shim.setStyle("z-index", zindex);
25884         }
25885     }
25886 });
25887 })();/*
25888  * Based on:
25889  * Ext JS Library 1.1.1
25890  * Copyright(c) 2006-2007, Ext JS, LLC.
25891  *
25892  * Originally Released Under LGPL - original licence link has changed is not relivant.
25893  *
25894  * Fork - LGPL
25895  * <script type="text/javascript">
25896  */
25897
25898
25899 /**
25900  * @class Roo.Shadow
25901  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25902  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25903  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25904  * @constructor
25905  * Create a new Shadow
25906  * @param {Object} config The config object
25907  */
25908 Roo.Shadow = function(config){
25909     Roo.apply(this, config);
25910     if(typeof this.mode != "string"){
25911         this.mode = this.defaultMode;
25912     }
25913     var o = this.offset, a = {h: 0};
25914     var rad = Math.floor(this.offset/2);
25915     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25916         case "drop":
25917             a.w = 0;
25918             a.l = a.t = o;
25919             a.t -= 1;
25920             if(Roo.isIE){
25921                 a.l -= this.offset + rad;
25922                 a.t -= this.offset + rad;
25923                 a.w -= rad;
25924                 a.h -= rad;
25925                 a.t += 1;
25926             }
25927         break;
25928         case "sides":
25929             a.w = (o*2);
25930             a.l = -o;
25931             a.t = o-1;
25932             if(Roo.isIE){
25933                 a.l -= (this.offset - rad);
25934                 a.t -= this.offset + rad;
25935                 a.l += 1;
25936                 a.w -= (this.offset - rad)*2;
25937                 a.w -= rad + 1;
25938                 a.h -= 1;
25939             }
25940         break;
25941         case "frame":
25942             a.w = a.h = (o*2);
25943             a.l = a.t = -o;
25944             a.t += 1;
25945             a.h -= 2;
25946             if(Roo.isIE){
25947                 a.l -= (this.offset - rad);
25948                 a.t -= (this.offset - rad);
25949                 a.l += 1;
25950                 a.w -= (this.offset + rad + 1);
25951                 a.h -= (this.offset + rad);
25952                 a.h += 1;
25953             }
25954         break;
25955     };
25956
25957     this.adjusts = a;
25958 };
25959
25960 Roo.Shadow.prototype = {
25961     /**
25962      * @cfg {String} mode
25963      * The shadow display mode.  Supports the following options:<br />
25964      * sides: Shadow displays on both sides and bottom only<br />
25965      * frame: Shadow displays equally on all four sides<br />
25966      * drop: Traditional bottom-right drop shadow (default)
25967      */
25968     /**
25969      * @cfg {String} offset
25970      * The number of pixels to offset the shadow from the element (defaults to 4)
25971      */
25972     offset: 4,
25973
25974     // private
25975     defaultMode: "drop",
25976
25977     /**
25978      * Displays the shadow under the target element
25979      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25980      */
25981     show : function(target){
25982         target = Roo.get(target);
25983         if(!this.el){
25984             this.el = Roo.Shadow.Pool.pull();
25985             if(this.el.dom.nextSibling != target.dom){
25986                 this.el.insertBefore(target);
25987             }
25988         }
25989         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25990         if(Roo.isIE){
25991             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25992         }
25993         this.realign(
25994             target.getLeft(true),
25995             target.getTop(true),
25996             target.getWidth(),
25997             target.getHeight()
25998         );
25999         this.el.dom.style.display = "block";
26000     },
26001
26002     /**
26003      * Returns true if the shadow is visible, else false
26004      */
26005     isVisible : function(){
26006         return this.el ? true : false;  
26007     },
26008
26009     /**
26010      * Direct alignment when values are already available. Show must be called at least once before
26011      * calling this method to ensure it is initialized.
26012      * @param {Number} left The target element left position
26013      * @param {Number} top The target element top position
26014      * @param {Number} width The target element width
26015      * @param {Number} height The target element height
26016      */
26017     realign : function(l, t, w, h){
26018         if(!this.el){
26019             return;
26020         }
26021         var a = this.adjusts, d = this.el.dom, s = d.style;
26022         var iea = 0;
26023         s.left = (l+a.l)+"px";
26024         s.top = (t+a.t)+"px";
26025         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26026  
26027         if(s.width != sws || s.height != shs){
26028             s.width = sws;
26029             s.height = shs;
26030             if(!Roo.isIE){
26031                 var cn = d.childNodes;
26032                 var sww = Math.max(0, (sw-12))+"px";
26033                 cn[0].childNodes[1].style.width = sww;
26034                 cn[1].childNodes[1].style.width = sww;
26035                 cn[2].childNodes[1].style.width = sww;
26036                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26037             }
26038         }
26039     },
26040
26041     /**
26042      * Hides this shadow
26043      */
26044     hide : function(){
26045         if(this.el){
26046             this.el.dom.style.display = "none";
26047             Roo.Shadow.Pool.push(this.el);
26048             delete this.el;
26049         }
26050     },
26051
26052     /**
26053      * Adjust the z-index of this shadow
26054      * @param {Number} zindex The new z-index
26055      */
26056     setZIndex : function(z){
26057         this.zIndex = z;
26058         if(this.el){
26059             this.el.setStyle("z-index", z);
26060         }
26061     }
26062 };
26063
26064 // Private utility class that manages the internal Shadow cache
26065 Roo.Shadow.Pool = function(){
26066     var p = [];
26067     var markup = Roo.isIE ?
26068                  '<div class="x-ie-shadow"></div>' :
26069                  '<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>';
26070     return {
26071         pull : function(){
26072             var sh = p.shift();
26073             if(!sh){
26074                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26075                 sh.autoBoxAdjust = false;
26076             }
26077             return sh;
26078         },
26079
26080         push : function(sh){
26081             p.push(sh);
26082         }
26083     };
26084 }();/*
26085  * Based on:
26086  * Ext JS Library 1.1.1
26087  * Copyright(c) 2006-2007, Ext JS, LLC.
26088  *
26089  * Originally Released Under LGPL - original licence link has changed is not relivant.
26090  *
26091  * Fork - LGPL
26092  * <script type="text/javascript">
26093  */
26094
26095
26096 /**
26097  * @class Roo.SplitBar
26098  * @extends Roo.util.Observable
26099  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26100  * <br><br>
26101  * Usage:
26102  * <pre><code>
26103 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26104                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26105 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26106 split.minSize = 100;
26107 split.maxSize = 600;
26108 split.animate = true;
26109 split.on('moved', splitterMoved);
26110 </code></pre>
26111  * @constructor
26112  * Create a new SplitBar
26113  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26114  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26115  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26116  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26117                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26118                         position of the SplitBar).
26119  */
26120 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26121     
26122     /** @private */
26123     this.el = Roo.get(dragElement, true);
26124     this.el.dom.unselectable = "on";
26125     /** @private */
26126     this.resizingEl = Roo.get(resizingElement, true);
26127
26128     /**
26129      * @private
26130      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26131      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26132      * @type Number
26133      */
26134     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26135     
26136     /**
26137      * The minimum size of the resizing element. (Defaults to 0)
26138      * @type Number
26139      */
26140     this.minSize = 0;
26141     
26142     /**
26143      * The maximum size of the resizing element. (Defaults to 2000)
26144      * @type Number
26145      */
26146     this.maxSize = 2000;
26147     
26148     /**
26149      * Whether to animate the transition to the new size
26150      * @type Boolean
26151      */
26152     this.animate = false;
26153     
26154     /**
26155      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26156      * @type Boolean
26157      */
26158     this.useShim = false;
26159     
26160     /** @private */
26161     this.shim = null;
26162     
26163     if(!existingProxy){
26164         /** @private */
26165         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26166     }else{
26167         this.proxy = Roo.get(existingProxy).dom;
26168     }
26169     /** @private */
26170     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26171     
26172     /** @private */
26173     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26174     
26175     /** @private */
26176     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26177     
26178     /** @private */
26179     this.dragSpecs = {};
26180     
26181     /**
26182      * @private The adapter to use to positon and resize elements
26183      */
26184     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26185     this.adapter.init(this);
26186     
26187     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26188         /** @private */
26189         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26190         this.el.addClass("x-splitbar-h");
26191     }else{
26192         /** @private */
26193         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26194         this.el.addClass("x-splitbar-v");
26195     }
26196     
26197     this.addEvents({
26198         /**
26199          * @event resize
26200          * Fires when the splitter is moved (alias for {@link #event-moved})
26201          * @param {Roo.SplitBar} this
26202          * @param {Number} newSize the new width or height
26203          */
26204         "resize" : true,
26205         /**
26206          * @event moved
26207          * Fires when the splitter is moved
26208          * @param {Roo.SplitBar} this
26209          * @param {Number} newSize the new width or height
26210          */
26211         "moved" : true,
26212         /**
26213          * @event beforeresize
26214          * Fires before the splitter is dragged
26215          * @param {Roo.SplitBar} this
26216          */
26217         "beforeresize" : true,
26218
26219         "beforeapply" : true
26220     });
26221
26222     Roo.util.Observable.call(this);
26223 };
26224
26225 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26226     onStartProxyDrag : function(x, y){
26227         this.fireEvent("beforeresize", this);
26228         if(!this.overlay){
26229             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26230             o.unselectable();
26231             o.enableDisplayMode("block");
26232             // all splitbars share the same overlay
26233             Roo.SplitBar.prototype.overlay = o;
26234         }
26235         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26236         this.overlay.show();
26237         Roo.get(this.proxy).setDisplayed("block");
26238         var size = this.adapter.getElementSize(this);
26239         this.activeMinSize = this.getMinimumSize();;
26240         this.activeMaxSize = this.getMaximumSize();;
26241         var c1 = size - this.activeMinSize;
26242         var c2 = Math.max(this.activeMaxSize - size, 0);
26243         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26244             this.dd.resetConstraints();
26245             this.dd.setXConstraint(
26246                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26247                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26248             );
26249             this.dd.setYConstraint(0, 0);
26250         }else{
26251             this.dd.resetConstraints();
26252             this.dd.setXConstraint(0, 0);
26253             this.dd.setYConstraint(
26254                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26255                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26256             );
26257          }
26258         this.dragSpecs.startSize = size;
26259         this.dragSpecs.startPoint = [x, y];
26260         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26261     },
26262     
26263     /** 
26264      * @private Called after the drag operation by the DDProxy
26265      */
26266     onEndProxyDrag : function(e){
26267         Roo.get(this.proxy).setDisplayed(false);
26268         var endPoint = Roo.lib.Event.getXY(e);
26269         if(this.overlay){
26270             this.overlay.hide();
26271         }
26272         var newSize;
26273         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26274             newSize = this.dragSpecs.startSize + 
26275                 (this.placement == Roo.SplitBar.LEFT ?
26276                     endPoint[0] - this.dragSpecs.startPoint[0] :
26277                     this.dragSpecs.startPoint[0] - endPoint[0]
26278                 );
26279         }else{
26280             newSize = this.dragSpecs.startSize + 
26281                 (this.placement == Roo.SplitBar.TOP ?
26282                     endPoint[1] - this.dragSpecs.startPoint[1] :
26283                     this.dragSpecs.startPoint[1] - endPoint[1]
26284                 );
26285         }
26286         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26287         if(newSize != this.dragSpecs.startSize){
26288             if(this.fireEvent('beforeapply', this, newSize) !== false){
26289                 this.adapter.setElementSize(this, newSize);
26290                 this.fireEvent("moved", this, newSize);
26291                 this.fireEvent("resize", this, newSize);
26292             }
26293         }
26294     },
26295     
26296     /**
26297      * Get the adapter this SplitBar uses
26298      * @return The adapter object
26299      */
26300     getAdapter : function(){
26301         return this.adapter;
26302     },
26303     
26304     /**
26305      * Set the adapter this SplitBar uses
26306      * @param {Object} adapter A SplitBar adapter object
26307      */
26308     setAdapter : function(adapter){
26309         this.adapter = adapter;
26310         this.adapter.init(this);
26311     },
26312     
26313     /**
26314      * Gets the minimum size for the resizing element
26315      * @return {Number} The minimum size
26316      */
26317     getMinimumSize : function(){
26318         return this.minSize;
26319     },
26320     
26321     /**
26322      * Sets the minimum size for the resizing element
26323      * @param {Number} minSize The minimum size
26324      */
26325     setMinimumSize : function(minSize){
26326         this.minSize = minSize;
26327     },
26328     
26329     /**
26330      * Gets the maximum size for the resizing element
26331      * @return {Number} The maximum size
26332      */
26333     getMaximumSize : function(){
26334         return this.maxSize;
26335     },
26336     
26337     /**
26338      * Sets the maximum size for the resizing element
26339      * @param {Number} maxSize The maximum size
26340      */
26341     setMaximumSize : function(maxSize){
26342         this.maxSize = maxSize;
26343     },
26344     
26345     /**
26346      * Sets the initialize size for the resizing element
26347      * @param {Number} size The initial size
26348      */
26349     setCurrentSize : function(size){
26350         var oldAnimate = this.animate;
26351         this.animate = false;
26352         this.adapter.setElementSize(this, size);
26353         this.animate = oldAnimate;
26354     },
26355     
26356     /**
26357      * Destroy this splitbar. 
26358      * @param {Boolean} removeEl True to remove the element
26359      */
26360     destroy : function(removeEl){
26361         if(this.shim){
26362             this.shim.remove();
26363         }
26364         this.dd.unreg();
26365         this.proxy.parentNode.removeChild(this.proxy);
26366         if(removeEl){
26367             this.el.remove();
26368         }
26369     }
26370 });
26371
26372 /**
26373  * @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.
26374  */
26375 Roo.SplitBar.createProxy = function(dir){
26376     var proxy = new Roo.Element(document.createElement("div"));
26377     proxy.unselectable();
26378     var cls = 'x-splitbar-proxy';
26379     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26380     document.body.appendChild(proxy.dom);
26381     return proxy.dom;
26382 };
26383
26384 /** 
26385  * @class Roo.SplitBar.BasicLayoutAdapter
26386  * Default Adapter. It assumes the splitter and resizing element are not positioned
26387  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26388  */
26389 Roo.SplitBar.BasicLayoutAdapter = function(){
26390 };
26391
26392 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26393     // do nothing for now
26394     init : function(s){
26395     
26396     },
26397     /**
26398      * Called before drag operations to get the current size of the resizing element. 
26399      * @param {Roo.SplitBar} s The SplitBar using this adapter
26400      */
26401      getElementSize : function(s){
26402         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26403             return s.resizingEl.getWidth();
26404         }else{
26405             return s.resizingEl.getHeight();
26406         }
26407     },
26408     
26409     /**
26410      * Called after drag operations to set the size of the resizing element.
26411      * @param {Roo.SplitBar} s The SplitBar using this adapter
26412      * @param {Number} newSize The new size to set
26413      * @param {Function} onComplete A function to be invoked when resizing is complete
26414      */
26415     setElementSize : function(s, newSize, onComplete){
26416         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26417             if(!s.animate){
26418                 s.resizingEl.setWidth(newSize);
26419                 if(onComplete){
26420                     onComplete(s, newSize);
26421                 }
26422             }else{
26423                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26424             }
26425         }else{
26426             
26427             if(!s.animate){
26428                 s.resizingEl.setHeight(newSize);
26429                 if(onComplete){
26430                     onComplete(s, newSize);
26431                 }
26432             }else{
26433                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26434             }
26435         }
26436     }
26437 };
26438
26439 /** 
26440  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26441  * @extends Roo.SplitBar.BasicLayoutAdapter
26442  * Adapter that  moves the splitter element to align with the resized sizing element. 
26443  * Used with an absolute positioned SplitBar.
26444  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26445  * document.body, make sure you assign an id to the body element.
26446  */
26447 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26448     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26449     this.container = Roo.get(container);
26450 };
26451
26452 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26453     init : function(s){
26454         this.basic.init(s);
26455     },
26456     
26457     getElementSize : function(s){
26458         return this.basic.getElementSize(s);
26459     },
26460     
26461     setElementSize : function(s, newSize, onComplete){
26462         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26463     },
26464     
26465     moveSplitter : function(s){
26466         var yes = Roo.SplitBar;
26467         switch(s.placement){
26468             case yes.LEFT:
26469                 s.el.setX(s.resizingEl.getRight());
26470                 break;
26471             case yes.RIGHT:
26472                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26473                 break;
26474             case yes.TOP:
26475                 s.el.setY(s.resizingEl.getBottom());
26476                 break;
26477             case yes.BOTTOM:
26478                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26479                 break;
26480         }
26481     }
26482 };
26483
26484 /**
26485  * Orientation constant - Create a vertical SplitBar
26486  * @static
26487  * @type Number
26488  */
26489 Roo.SplitBar.VERTICAL = 1;
26490
26491 /**
26492  * Orientation constant - Create a horizontal SplitBar
26493  * @static
26494  * @type Number
26495  */
26496 Roo.SplitBar.HORIZONTAL = 2;
26497
26498 /**
26499  * Placement constant - The resizing element is to the left of the splitter element
26500  * @static
26501  * @type Number
26502  */
26503 Roo.SplitBar.LEFT = 1;
26504
26505 /**
26506  * Placement constant - The resizing element is to the right of the splitter element
26507  * @static
26508  * @type Number
26509  */
26510 Roo.SplitBar.RIGHT = 2;
26511
26512 /**
26513  * Placement constant - The resizing element is positioned above the splitter element
26514  * @static
26515  * @type Number
26516  */
26517 Roo.SplitBar.TOP = 3;
26518
26519 /**
26520  * Placement constant - The resizing element is positioned under splitter element
26521  * @static
26522  * @type Number
26523  */
26524 Roo.SplitBar.BOTTOM = 4;
26525 /*
26526  * Based on:
26527  * Ext JS Library 1.1.1
26528  * Copyright(c) 2006-2007, Ext JS, LLC.
26529  *
26530  * Originally Released Under LGPL - original licence link has changed is not relivant.
26531  *
26532  * Fork - LGPL
26533  * <script type="text/javascript">
26534  */
26535
26536 /**
26537  * @class Roo.View
26538  * @extends Roo.util.Observable
26539  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26540  * This class also supports single and multi selection modes. <br>
26541  * Create a data model bound view:
26542  <pre><code>
26543  var store = new Roo.data.Store(...);
26544
26545  var view = new Roo.View({
26546     el : "my-element",
26547     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26548  
26549     singleSelect: true,
26550     selectedClass: "ydataview-selected",
26551     store: store
26552  });
26553
26554  // listen for node click?
26555  view.on("click", function(vw, index, node, e){
26556  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26557  });
26558
26559  // load XML data
26560  dataModel.load("foobar.xml");
26561  </code></pre>
26562  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26563  * <br><br>
26564  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26565  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26566  * 
26567  * Note: old style constructor is still suported (container, template, config)
26568  * 
26569  * @constructor
26570  * Create a new View
26571  * @param {Object} config The config object
26572  * 
26573  */
26574 Roo.View = function(config, depreciated_tpl, depreciated_config){
26575     
26576     this.parent = false;
26577     
26578     if (typeof(depreciated_tpl) == 'undefined') {
26579         // new way.. - universal constructor.
26580         Roo.apply(this, config);
26581         this.el  = Roo.get(this.el);
26582     } else {
26583         // old format..
26584         this.el  = Roo.get(config);
26585         this.tpl = depreciated_tpl;
26586         Roo.apply(this, depreciated_config);
26587     }
26588     this.wrapEl  = this.el.wrap().wrap();
26589     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26590     
26591     
26592     if(typeof(this.tpl) == "string"){
26593         this.tpl = new Roo.Template(this.tpl);
26594     } else {
26595         // support xtype ctors..
26596         this.tpl = new Roo.factory(this.tpl, Roo);
26597     }
26598     
26599     
26600     this.tpl.compile();
26601     
26602     /** @private */
26603     this.addEvents({
26604         /**
26605          * @event beforeclick
26606          * Fires before a click is processed. Returns false to cancel the default action.
26607          * @param {Roo.View} this
26608          * @param {Number} index The index of the target node
26609          * @param {HTMLElement} node The target node
26610          * @param {Roo.EventObject} e The raw event object
26611          */
26612             "beforeclick" : true,
26613         /**
26614          * @event click
26615          * Fires when a template node is clicked.
26616          * @param {Roo.View} this
26617          * @param {Number} index The index of the target node
26618          * @param {HTMLElement} node The target node
26619          * @param {Roo.EventObject} e The raw event object
26620          */
26621             "click" : true,
26622         /**
26623          * @event dblclick
26624          * Fires when a template node is double clicked.
26625          * @param {Roo.View} this
26626          * @param {Number} index The index of the target node
26627          * @param {HTMLElement} node The target node
26628          * @param {Roo.EventObject} e The raw event object
26629          */
26630             "dblclick" : true,
26631         /**
26632          * @event contextmenu
26633          * Fires when a template node is right clicked.
26634          * @param {Roo.View} this
26635          * @param {Number} index The index of the target node
26636          * @param {HTMLElement} node The target node
26637          * @param {Roo.EventObject} e The raw event object
26638          */
26639             "contextmenu" : true,
26640         /**
26641          * @event selectionchange
26642          * Fires when the selected nodes change.
26643          * @param {Roo.View} this
26644          * @param {Array} selections Array of the selected nodes
26645          */
26646             "selectionchange" : true,
26647     
26648         /**
26649          * @event beforeselect
26650          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26651          * @param {Roo.View} this
26652          * @param {HTMLElement} node The node to be selected
26653          * @param {Array} selections Array of currently selected nodes
26654          */
26655             "beforeselect" : true,
26656         /**
26657          * @event preparedata
26658          * Fires on every row to render, to allow you to change the data.
26659          * @param {Roo.View} this
26660          * @param {Object} data to be rendered (change this)
26661          */
26662           "preparedata" : true
26663           
26664           
26665         });
26666
26667
26668
26669     this.el.on({
26670         "click": this.onClick,
26671         "dblclick": this.onDblClick,
26672         "contextmenu": this.onContextMenu,
26673         scope:this
26674     });
26675
26676     this.selections = [];
26677     this.nodes = [];
26678     this.cmp = new Roo.CompositeElementLite([]);
26679     if(this.store){
26680         this.store = Roo.factory(this.store, Roo.data);
26681         this.setStore(this.store, true);
26682     }
26683     
26684     if ( this.footer && this.footer.xtype) {
26685            
26686          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26687         
26688         this.footer.dataSource = this.store;
26689         this.footer.container = fctr;
26690         this.footer = Roo.factory(this.footer, Roo);
26691         fctr.insertFirst(this.el);
26692         
26693         // this is a bit insane - as the paging toolbar seems to detach the el..
26694 //        dom.parentNode.parentNode.parentNode
26695          // they get detached?
26696     }
26697     
26698     
26699     Roo.View.superclass.constructor.call(this);
26700     
26701     
26702 };
26703
26704 Roo.extend(Roo.View, Roo.util.Observable, {
26705     
26706      /**
26707      * @cfg {Roo.data.Store} store Data store to load data from.
26708      */
26709     store : false,
26710     
26711     /**
26712      * @cfg {String|Roo.Element} el The container element.
26713      */
26714     el : '',
26715     
26716     /**
26717      * @cfg {String|Roo.Template} tpl The template used by this View 
26718      */
26719     tpl : false,
26720     /**
26721      * @cfg {String} dataName the named area of the template to use as the data area
26722      *                          Works with domtemplates roo-name="name"
26723      */
26724     dataName: false,
26725     /**
26726      * @cfg {String} selectedClass The css class to add to selected nodes
26727      */
26728     selectedClass : "x-view-selected",
26729      /**
26730      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26731      */
26732     emptyText : "",
26733     
26734     /**
26735      * @cfg {String} text to display on mask (default Loading)
26736      */
26737     mask : false,
26738     /**
26739      * @cfg {Boolean} multiSelect Allow multiple selection
26740      */
26741     multiSelect : false,
26742     /**
26743      * @cfg {Boolean} singleSelect Allow single selection
26744      */
26745     singleSelect:  false,
26746     
26747     /**
26748      * @cfg {Boolean} toggleSelect - selecting 
26749      */
26750     toggleSelect : false,
26751     
26752     /**
26753      * @cfg {Boolean} tickable - selecting 
26754      */
26755     tickable : false,
26756     
26757     /**
26758      * Returns the element this view is bound to.
26759      * @return {Roo.Element}
26760      */
26761     getEl : function(){
26762         return this.wrapEl;
26763     },
26764     
26765     
26766
26767     /**
26768      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26769      */
26770     refresh : function(){
26771         //Roo.log('refresh');
26772         var t = this.tpl;
26773         
26774         // if we are using something like 'domtemplate', then
26775         // the what gets used is:
26776         // t.applySubtemplate(NAME, data, wrapping data..)
26777         // the outer template then get' applied with
26778         //     the store 'extra data'
26779         // and the body get's added to the
26780         //      roo-name="data" node?
26781         //      <span class='roo-tpl-{name}'></span> ?????
26782         
26783         
26784         
26785         this.clearSelections();
26786         this.el.update("");
26787         var html = [];
26788         var records = this.store.getRange();
26789         if(records.length < 1) {
26790             
26791             // is this valid??  = should it render a template??
26792             
26793             this.el.update(this.emptyText);
26794             return;
26795         }
26796         var el = this.el;
26797         if (this.dataName) {
26798             this.el.update(t.apply(this.store.meta)); //????
26799             el = this.el.child('.roo-tpl-' + this.dataName);
26800         }
26801         
26802         for(var i = 0, len = records.length; i < len; i++){
26803             var data = this.prepareData(records[i].data, i, records[i]);
26804             this.fireEvent("preparedata", this, data, i, records[i]);
26805             
26806             var d = Roo.apply({}, data);
26807             
26808             if(this.tickable){
26809                 Roo.apply(d, {'roo-id' : Roo.id()});
26810                 
26811                 var _this = this;
26812             
26813                 Roo.each(this.parent.item, function(item){
26814                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26815                         return;
26816                     }
26817                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26818                 });
26819             }
26820             
26821             html[html.length] = Roo.util.Format.trim(
26822                 this.dataName ?
26823                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26824                     t.apply(d)
26825             );
26826         }
26827         
26828         
26829         
26830         el.update(html.join(""));
26831         this.nodes = el.dom.childNodes;
26832         this.updateIndexes(0);
26833     },
26834     
26835
26836     /**
26837      * Function to override to reformat the data that is sent to
26838      * the template for each node.
26839      * DEPRICATED - use the preparedata event handler.
26840      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26841      * a JSON object for an UpdateManager bound view).
26842      */
26843     prepareData : function(data, index, record)
26844     {
26845         this.fireEvent("preparedata", this, data, index, record);
26846         return data;
26847     },
26848
26849     onUpdate : function(ds, record){
26850         // Roo.log('on update');   
26851         this.clearSelections();
26852         var index = this.store.indexOf(record);
26853         var n = this.nodes[index];
26854         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26855         n.parentNode.removeChild(n);
26856         this.updateIndexes(index, index);
26857     },
26858
26859     
26860     
26861 // --------- FIXME     
26862     onAdd : function(ds, records, index)
26863     {
26864         //Roo.log(['on Add', ds, records, index] );        
26865         this.clearSelections();
26866         if(this.nodes.length == 0){
26867             this.refresh();
26868             return;
26869         }
26870         var n = this.nodes[index];
26871         for(var i = 0, len = records.length; i < len; i++){
26872             var d = this.prepareData(records[i].data, i, records[i]);
26873             if(n){
26874                 this.tpl.insertBefore(n, d);
26875             }else{
26876                 
26877                 this.tpl.append(this.el, d);
26878             }
26879         }
26880         this.updateIndexes(index);
26881     },
26882
26883     onRemove : function(ds, record, index){
26884        // Roo.log('onRemove');
26885         this.clearSelections();
26886         var el = this.dataName  ?
26887             this.el.child('.roo-tpl-' + this.dataName) :
26888             this.el; 
26889         
26890         el.dom.removeChild(this.nodes[index]);
26891         this.updateIndexes(index);
26892     },
26893
26894     /**
26895      * Refresh an individual node.
26896      * @param {Number} index
26897      */
26898     refreshNode : function(index){
26899         this.onUpdate(this.store, this.store.getAt(index));
26900     },
26901
26902     updateIndexes : function(startIndex, endIndex){
26903         var ns = this.nodes;
26904         startIndex = startIndex || 0;
26905         endIndex = endIndex || ns.length - 1;
26906         for(var i = startIndex; i <= endIndex; i++){
26907             ns[i].nodeIndex = i;
26908         }
26909     },
26910
26911     /**
26912      * Changes the data store this view uses and refresh the view.
26913      * @param {Store} store
26914      */
26915     setStore : function(store, initial){
26916         if(!initial && this.store){
26917             this.store.un("datachanged", this.refresh);
26918             this.store.un("add", this.onAdd);
26919             this.store.un("remove", this.onRemove);
26920             this.store.un("update", this.onUpdate);
26921             this.store.un("clear", this.refresh);
26922             this.store.un("beforeload", this.onBeforeLoad);
26923             this.store.un("load", this.onLoad);
26924             this.store.un("loadexception", this.onLoad);
26925         }
26926         if(store){
26927           
26928             store.on("datachanged", this.refresh, this);
26929             store.on("add", this.onAdd, this);
26930             store.on("remove", this.onRemove, this);
26931             store.on("update", this.onUpdate, this);
26932             store.on("clear", this.refresh, this);
26933             store.on("beforeload", this.onBeforeLoad, this);
26934             store.on("load", this.onLoad, this);
26935             store.on("loadexception", this.onLoad, this);
26936         }
26937         
26938         if(store){
26939             this.refresh();
26940         }
26941     },
26942     /**
26943      * onbeforeLoad - masks the loading area.
26944      *
26945      */
26946     onBeforeLoad : function(store,opts)
26947     {
26948          //Roo.log('onBeforeLoad');   
26949         if (!opts.add) {
26950             this.el.update("");
26951         }
26952         this.el.mask(this.mask ? this.mask : "Loading" ); 
26953     },
26954     onLoad : function ()
26955     {
26956         this.el.unmask();
26957     },
26958     
26959
26960     /**
26961      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26962      * @param {HTMLElement} node
26963      * @return {HTMLElement} The template node
26964      */
26965     findItemFromChild : function(node){
26966         var el = this.dataName  ?
26967             this.el.child('.roo-tpl-' + this.dataName,true) :
26968             this.el.dom; 
26969         
26970         if(!node || node.parentNode == el){
26971                     return node;
26972             }
26973             var p = node.parentNode;
26974             while(p && p != el){
26975             if(p.parentNode == el){
26976                 return p;
26977             }
26978             p = p.parentNode;
26979         }
26980             return null;
26981     },
26982
26983     /** @ignore */
26984     onClick : function(e){
26985         var item = this.findItemFromChild(e.getTarget());
26986         if(item){
26987             var index = this.indexOf(item);
26988             if(this.onItemClick(item, index, e) !== false){
26989                 this.fireEvent("click", this, index, item, e);
26990             }
26991         }else{
26992             this.clearSelections();
26993         }
26994     },
26995
26996     /** @ignore */
26997     onContextMenu : function(e){
26998         var item = this.findItemFromChild(e.getTarget());
26999         if(item){
27000             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27001         }
27002     },
27003
27004     /** @ignore */
27005     onDblClick : function(e){
27006         var item = this.findItemFromChild(e.getTarget());
27007         if(item){
27008             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27009         }
27010     },
27011
27012     onItemClick : function(item, index, e)
27013     {
27014         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27015             return false;
27016         }
27017         if (this.toggleSelect) {
27018             var m = this.isSelected(item) ? 'unselect' : 'select';
27019             //Roo.log(m);
27020             var _t = this;
27021             _t[m](item, true, false);
27022             return true;
27023         }
27024         if(this.multiSelect || this.singleSelect){
27025             if(this.multiSelect && e.shiftKey && this.lastSelection){
27026                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27027             }else{
27028                 this.select(item, this.multiSelect && e.ctrlKey);
27029                 this.lastSelection = item;
27030             }
27031             
27032             if(!this.tickable){
27033                 e.preventDefault();
27034             }
27035             
27036         }
27037         return true;
27038     },
27039
27040     /**
27041      * Get the number of selected nodes.
27042      * @return {Number}
27043      */
27044     getSelectionCount : function(){
27045         return this.selections.length;
27046     },
27047
27048     /**
27049      * Get the currently selected nodes.
27050      * @return {Array} An array of HTMLElements
27051      */
27052     getSelectedNodes : function(){
27053         return this.selections;
27054     },
27055
27056     /**
27057      * Get the indexes of the selected nodes.
27058      * @return {Array}
27059      */
27060     getSelectedIndexes : function(){
27061         var indexes = [], s = this.selections;
27062         for(var i = 0, len = s.length; i < len; i++){
27063             indexes.push(s[i].nodeIndex);
27064         }
27065         return indexes;
27066     },
27067
27068     /**
27069      * Clear all selections
27070      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27071      */
27072     clearSelections : function(suppressEvent){
27073         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27074             this.cmp.elements = this.selections;
27075             this.cmp.removeClass(this.selectedClass);
27076             this.selections = [];
27077             if(!suppressEvent){
27078                 this.fireEvent("selectionchange", this, this.selections);
27079             }
27080         }
27081     },
27082
27083     /**
27084      * Returns true if the passed node is selected
27085      * @param {HTMLElement/Number} node The node or node index
27086      * @return {Boolean}
27087      */
27088     isSelected : function(node){
27089         var s = this.selections;
27090         if(s.length < 1){
27091             return false;
27092         }
27093         node = this.getNode(node);
27094         return s.indexOf(node) !== -1;
27095     },
27096
27097     /**
27098      * Selects nodes.
27099      * @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
27100      * @param {Boolean} keepExisting (optional) true to keep existing selections
27101      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27102      */
27103     select : function(nodeInfo, keepExisting, suppressEvent){
27104         if(nodeInfo instanceof Array){
27105             if(!keepExisting){
27106                 this.clearSelections(true);
27107             }
27108             for(var i = 0, len = nodeInfo.length; i < len; i++){
27109                 this.select(nodeInfo[i], true, true);
27110             }
27111             return;
27112         } 
27113         var node = this.getNode(nodeInfo);
27114         if(!node || this.isSelected(node)){
27115             return; // already selected.
27116         }
27117         if(!keepExisting){
27118             this.clearSelections(true);
27119         }
27120         
27121         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27122             Roo.fly(node).addClass(this.selectedClass);
27123             this.selections.push(node);
27124             if(!suppressEvent){
27125                 this.fireEvent("selectionchange", this, this.selections);
27126             }
27127         }
27128         
27129         
27130     },
27131       /**
27132      * Unselects nodes.
27133      * @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
27134      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27135      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27136      */
27137     unselect : function(nodeInfo, keepExisting, suppressEvent)
27138     {
27139         if(nodeInfo instanceof Array){
27140             Roo.each(this.selections, function(s) {
27141                 this.unselect(s, nodeInfo);
27142             }, this);
27143             return;
27144         }
27145         var node = this.getNode(nodeInfo);
27146         if(!node || !this.isSelected(node)){
27147             //Roo.log("not selected");
27148             return; // not selected.
27149         }
27150         // fireevent???
27151         var ns = [];
27152         Roo.each(this.selections, function(s) {
27153             if (s == node ) {
27154                 Roo.fly(node).removeClass(this.selectedClass);
27155
27156                 return;
27157             }
27158             ns.push(s);
27159         },this);
27160         
27161         this.selections= ns;
27162         this.fireEvent("selectionchange", this, this.selections);
27163     },
27164
27165     /**
27166      * Gets a template 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 {HTMLElement} The node or null if it wasn't found
27169      */
27170     getNode : function(nodeInfo){
27171         if(typeof nodeInfo == "string"){
27172             return document.getElementById(nodeInfo);
27173         }else if(typeof nodeInfo == "number"){
27174             return this.nodes[nodeInfo];
27175         }
27176         return nodeInfo;
27177     },
27178
27179     /**
27180      * Gets a range template nodes.
27181      * @param {Number} startIndex
27182      * @param {Number} endIndex
27183      * @return {Array} An array of nodes
27184      */
27185     getNodes : function(start, end){
27186         var ns = this.nodes;
27187         start = start || 0;
27188         end = typeof end == "undefined" ? ns.length - 1 : end;
27189         var nodes = [];
27190         if(start <= end){
27191             for(var i = start; i <= end; i++){
27192                 nodes.push(ns[i]);
27193             }
27194         } else{
27195             for(var i = start; i >= end; i--){
27196                 nodes.push(ns[i]);
27197             }
27198         }
27199         return nodes;
27200     },
27201
27202     /**
27203      * Finds the index of the passed node
27204      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27205      * @return {Number} The index of the node or -1
27206      */
27207     indexOf : function(node){
27208         node = this.getNode(node);
27209         if(typeof node.nodeIndex == "number"){
27210             return node.nodeIndex;
27211         }
27212         var ns = this.nodes;
27213         for(var i = 0, len = ns.length; i < len; i++){
27214             if(ns[i] == node){
27215                 return i;
27216             }
27217         }
27218         return -1;
27219     }
27220 });
27221 /*
27222  * Based on:
27223  * Ext JS Library 1.1.1
27224  * Copyright(c) 2006-2007, Ext JS, LLC.
27225  *
27226  * Originally Released Under LGPL - original licence link has changed is not relivant.
27227  *
27228  * Fork - LGPL
27229  * <script type="text/javascript">
27230  */
27231
27232 /**
27233  * @class Roo.JsonView
27234  * @extends Roo.View
27235  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27236 <pre><code>
27237 var view = new Roo.JsonView({
27238     container: "my-element",
27239     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27240     multiSelect: true, 
27241     jsonRoot: "data" 
27242 });
27243
27244 // listen for node click?
27245 view.on("click", function(vw, index, node, e){
27246     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27247 });
27248
27249 // direct load of JSON data
27250 view.load("foobar.php");
27251
27252 // Example from my blog list
27253 var tpl = new Roo.Template(
27254     '&lt;div class="entry"&gt;' +
27255     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27256     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27257     "&lt;/div&gt;&lt;hr /&gt;"
27258 );
27259
27260 var moreView = new Roo.JsonView({
27261     container :  "entry-list", 
27262     template : tpl,
27263     jsonRoot: "posts"
27264 });
27265 moreView.on("beforerender", this.sortEntries, this);
27266 moreView.load({
27267     url: "/blog/get-posts.php",
27268     params: "allposts=true",
27269     text: "Loading Blog Entries..."
27270 });
27271 </code></pre>
27272
27273 * Note: old code is supported with arguments : (container, template, config)
27274
27275
27276  * @constructor
27277  * Create a new JsonView
27278  * 
27279  * @param {Object} config The config object
27280  * 
27281  */
27282 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27283     
27284     
27285     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27286
27287     var um = this.el.getUpdateManager();
27288     um.setRenderer(this);
27289     um.on("update", this.onLoad, this);
27290     um.on("failure", this.onLoadException, this);
27291
27292     /**
27293      * @event beforerender
27294      * Fires before rendering of the downloaded JSON data.
27295      * @param {Roo.JsonView} this
27296      * @param {Object} data The JSON data loaded
27297      */
27298     /**
27299      * @event load
27300      * Fires when data is loaded.
27301      * @param {Roo.JsonView} this
27302      * @param {Object} data The JSON data loaded
27303      * @param {Object} response The raw Connect response object
27304      */
27305     /**
27306      * @event loadexception
27307      * Fires when loading fails.
27308      * @param {Roo.JsonView} this
27309      * @param {Object} response The raw Connect response object
27310      */
27311     this.addEvents({
27312         'beforerender' : true,
27313         'load' : true,
27314         'loadexception' : true
27315     });
27316 };
27317 Roo.extend(Roo.JsonView, Roo.View, {
27318     /**
27319      * @type {String} The root property in the loaded JSON object that contains the data
27320      */
27321     jsonRoot : "",
27322
27323     /**
27324      * Refreshes the view.
27325      */
27326     refresh : function(){
27327         this.clearSelections();
27328         this.el.update("");
27329         var html = [];
27330         var o = this.jsonData;
27331         if(o && o.length > 0){
27332             for(var i = 0, len = o.length; i < len; i++){
27333                 var data = this.prepareData(o[i], i, o);
27334                 html[html.length] = this.tpl.apply(data);
27335             }
27336         }else{
27337             html.push(this.emptyText);
27338         }
27339         this.el.update(html.join(""));
27340         this.nodes = this.el.dom.childNodes;
27341         this.updateIndexes(0);
27342     },
27343
27344     /**
27345      * 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.
27346      * @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:
27347      <pre><code>
27348      view.load({
27349          url: "your-url.php",
27350          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27351          callback: yourFunction,
27352          scope: yourObject, //(optional scope)
27353          discardUrl: false,
27354          nocache: false,
27355          text: "Loading...",
27356          timeout: 30,
27357          scripts: false
27358      });
27359      </code></pre>
27360      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27361      * 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.
27362      * @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}
27363      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27364      * @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.
27365      */
27366     load : function(){
27367         var um = this.el.getUpdateManager();
27368         um.update.apply(um, arguments);
27369     },
27370
27371     // note - render is a standard framework call...
27372     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27373     render : function(el, response){
27374         
27375         this.clearSelections();
27376         this.el.update("");
27377         var o;
27378         try{
27379             if (response != '') {
27380                 o = Roo.util.JSON.decode(response.responseText);
27381                 if(this.jsonRoot){
27382                     
27383                     o = o[this.jsonRoot];
27384                 }
27385             }
27386         } catch(e){
27387         }
27388         /**
27389          * The current JSON data or null
27390          */
27391         this.jsonData = o;
27392         this.beforeRender();
27393         this.refresh();
27394     },
27395
27396 /**
27397  * Get the number of records in the current JSON dataset
27398  * @return {Number}
27399  */
27400     getCount : function(){
27401         return this.jsonData ? this.jsonData.length : 0;
27402     },
27403
27404 /**
27405  * Returns the JSON object for the specified node(s)
27406  * @param {HTMLElement/Array} node The node or an array of nodes
27407  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27408  * you get the JSON object for the node
27409  */
27410     getNodeData : function(node){
27411         if(node instanceof Array){
27412             var data = [];
27413             for(var i = 0, len = node.length; i < len; i++){
27414                 data.push(this.getNodeData(node[i]));
27415             }
27416             return data;
27417         }
27418         return this.jsonData[this.indexOf(node)] || null;
27419     },
27420
27421     beforeRender : function(){
27422         this.snapshot = this.jsonData;
27423         if(this.sortInfo){
27424             this.sort.apply(this, this.sortInfo);
27425         }
27426         this.fireEvent("beforerender", this, this.jsonData);
27427     },
27428
27429     onLoad : function(el, o){
27430         this.fireEvent("load", this, this.jsonData, o);
27431     },
27432
27433     onLoadException : function(el, o){
27434         this.fireEvent("loadexception", this, o);
27435     },
27436
27437 /**
27438  * Filter the data by a specific property.
27439  * @param {String} property A property on your JSON objects
27440  * @param {String/RegExp} value Either string that the property values
27441  * should start with, or a RegExp to test against the property
27442  */
27443     filter : function(property, value){
27444         if(this.jsonData){
27445             var data = [];
27446             var ss = this.snapshot;
27447             if(typeof value == "string"){
27448                 var vlen = value.length;
27449                 if(vlen == 0){
27450                     this.clearFilter();
27451                     return;
27452                 }
27453                 value = value.toLowerCase();
27454                 for(var i = 0, len = ss.length; i < len; i++){
27455                     var o = ss[i];
27456                     if(o[property].substr(0, vlen).toLowerCase() == value){
27457                         data.push(o);
27458                     }
27459                 }
27460             } else if(value.exec){ // regex?
27461                 for(var i = 0, len = ss.length; i < len; i++){
27462                     var o = ss[i];
27463                     if(value.test(o[property])){
27464                         data.push(o);
27465                     }
27466                 }
27467             } else{
27468                 return;
27469             }
27470             this.jsonData = data;
27471             this.refresh();
27472         }
27473     },
27474
27475 /**
27476  * Filter by a function. The passed function will be called with each
27477  * object in the current dataset. If the function returns true the value is kept,
27478  * otherwise it is filtered.
27479  * @param {Function} fn
27480  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27481  */
27482     filterBy : function(fn, scope){
27483         if(this.jsonData){
27484             var data = [];
27485             var ss = this.snapshot;
27486             for(var i = 0, len = ss.length; i < len; i++){
27487                 var o = ss[i];
27488                 if(fn.call(scope || this, o)){
27489                     data.push(o);
27490                 }
27491             }
27492             this.jsonData = data;
27493             this.refresh();
27494         }
27495     },
27496
27497 /**
27498  * Clears the current filter.
27499  */
27500     clearFilter : function(){
27501         if(this.snapshot && this.jsonData != this.snapshot){
27502             this.jsonData = this.snapshot;
27503             this.refresh();
27504         }
27505     },
27506
27507
27508 /**
27509  * Sorts the data for this view and refreshes it.
27510  * @param {String} property A property on your JSON objects to sort on
27511  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27512  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27513  */
27514     sort : function(property, dir, sortType){
27515         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27516         if(this.jsonData){
27517             var p = property;
27518             var dsc = dir && dir.toLowerCase() == "desc";
27519             var f = function(o1, o2){
27520                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27521                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27522                 ;
27523                 if(v1 < v2){
27524                     return dsc ? +1 : -1;
27525                 } else if(v1 > v2){
27526                     return dsc ? -1 : +1;
27527                 } else{
27528                     return 0;
27529                 }
27530             };
27531             this.jsonData.sort(f);
27532             this.refresh();
27533             if(this.jsonData != this.snapshot){
27534                 this.snapshot.sort(f);
27535             }
27536         }
27537     }
27538 });/*
27539  * Based on:
27540  * Ext JS Library 1.1.1
27541  * Copyright(c) 2006-2007, Ext JS, LLC.
27542  *
27543  * Originally Released Under LGPL - original licence link has changed is not relivant.
27544  *
27545  * Fork - LGPL
27546  * <script type="text/javascript">
27547  */
27548  
27549
27550 /**
27551  * @class Roo.ColorPalette
27552  * @extends Roo.Component
27553  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27554  * Here's an example of typical usage:
27555  * <pre><code>
27556 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27557 cp.render('my-div');
27558
27559 cp.on('select', function(palette, selColor){
27560     // do something with selColor
27561 });
27562 </code></pre>
27563  * @constructor
27564  * Create a new ColorPalette
27565  * @param {Object} config The config object
27566  */
27567 Roo.ColorPalette = function(config){
27568     Roo.ColorPalette.superclass.constructor.call(this, config);
27569     this.addEvents({
27570         /**
27571              * @event select
27572              * Fires when a color is selected
27573              * @param {ColorPalette} this
27574              * @param {String} color The 6-digit color hex code (without the # symbol)
27575              */
27576         select: true
27577     });
27578
27579     if(this.handler){
27580         this.on("select", this.handler, this.scope, true);
27581     }
27582 };
27583 Roo.extend(Roo.ColorPalette, Roo.Component, {
27584     /**
27585      * @cfg {String} itemCls
27586      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27587      */
27588     itemCls : "x-color-palette",
27589     /**
27590      * @cfg {String} value
27591      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27592      * the hex codes are case-sensitive.
27593      */
27594     value : null,
27595     clickEvent:'click',
27596     // private
27597     ctype: "Roo.ColorPalette",
27598
27599     /**
27600      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27601      */
27602     allowReselect : false,
27603
27604     /**
27605      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27606      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27607      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27608      * of colors with the width setting until the box is symmetrical.</p>
27609      * <p>You can override individual colors if needed:</p>
27610      * <pre><code>
27611 var cp = new Roo.ColorPalette();
27612 cp.colors[0] = "FF0000";  // change the first box to red
27613 </code></pre>
27614
27615 Or you can provide a custom array of your own for complete control:
27616 <pre><code>
27617 var cp = new Roo.ColorPalette();
27618 cp.colors = ["000000", "993300", "333300"];
27619 </code></pre>
27620      * @type Array
27621      */
27622     colors : [
27623         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27624         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27625         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27626         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27627         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27628     ],
27629
27630     // private
27631     onRender : function(container, position){
27632         var t = new Roo.MasterTemplate(
27633             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27634         );
27635         var c = this.colors;
27636         for(var i = 0, len = c.length; i < len; i++){
27637             t.add([c[i]]);
27638         }
27639         var el = document.createElement("div");
27640         el.className = this.itemCls;
27641         t.overwrite(el);
27642         container.dom.insertBefore(el, position);
27643         this.el = Roo.get(el);
27644         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27645         if(this.clickEvent != 'click'){
27646             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27647         }
27648     },
27649
27650     // private
27651     afterRender : function(){
27652         Roo.ColorPalette.superclass.afterRender.call(this);
27653         if(this.value){
27654             var s = this.value;
27655             this.value = null;
27656             this.select(s);
27657         }
27658     },
27659
27660     // private
27661     handleClick : function(e, t){
27662         e.preventDefault();
27663         if(!this.disabled){
27664             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27665             this.select(c.toUpperCase());
27666         }
27667     },
27668
27669     /**
27670      * Selects the specified color in the palette (fires the select event)
27671      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27672      */
27673     select : function(color){
27674         color = color.replace("#", "");
27675         if(color != this.value || this.allowReselect){
27676             var el = this.el;
27677             if(this.value){
27678                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27679             }
27680             el.child("a.color-"+color).addClass("x-color-palette-sel");
27681             this.value = color;
27682             this.fireEvent("select", this, color);
27683         }
27684     }
27685 });/*
27686  * Based on:
27687  * Ext JS Library 1.1.1
27688  * Copyright(c) 2006-2007, Ext JS, LLC.
27689  *
27690  * Originally Released Under LGPL - original licence link has changed is not relivant.
27691  *
27692  * Fork - LGPL
27693  * <script type="text/javascript">
27694  */
27695  
27696 /**
27697  * @class Roo.DatePicker
27698  * @extends Roo.Component
27699  * Simple date picker class.
27700  * @constructor
27701  * Create a new DatePicker
27702  * @param {Object} config The config object
27703  */
27704 Roo.DatePicker = function(config){
27705     Roo.DatePicker.superclass.constructor.call(this, config);
27706
27707     this.value = config && config.value ?
27708                  config.value.clearTime() : new Date().clearTime();
27709
27710     this.addEvents({
27711         /**
27712              * @event select
27713              * Fires when a date is selected
27714              * @param {DatePicker} this
27715              * @param {Date} date The selected date
27716              */
27717         'select': true,
27718         /**
27719              * @event monthchange
27720              * Fires when the displayed month changes 
27721              * @param {DatePicker} this
27722              * @param {Date} date The selected month
27723              */
27724         'monthchange': true
27725     });
27726
27727     if(this.handler){
27728         this.on("select", this.handler,  this.scope || this);
27729     }
27730     // build the disabledDatesRE
27731     if(!this.disabledDatesRE && this.disabledDates){
27732         var dd = this.disabledDates;
27733         var re = "(?:";
27734         for(var i = 0; i < dd.length; i++){
27735             re += dd[i];
27736             if(i != dd.length-1) {
27737                 re += "|";
27738             }
27739         }
27740         this.disabledDatesRE = new RegExp(re + ")");
27741     }
27742 };
27743
27744 Roo.extend(Roo.DatePicker, Roo.Component, {
27745     /**
27746      * @cfg {String} todayText
27747      * The text to display on the button that selects the current date (defaults to "Today")
27748      */
27749     todayText : "Today",
27750     /**
27751      * @cfg {String} okText
27752      * The text to display on the ok button
27753      */
27754     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27755     /**
27756      * @cfg {String} cancelText
27757      * The text to display on the cancel button
27758      */
27759     cancelText : "Cancel",
27760     /**
27761      * @cfg {String} todayTip
27762      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27763      */
27764     todayTip : "{0} (Spacebar)",
27765     /**
27766      * @cfg {Date} minDate
27767      * Minimum allowable date (JavaScript date object, defaults to null)
27768      */
27769     minDate : null,
27770     /**
27771      * @cfg {Date} maxDate
27772      * Maximum allowable date (JavaScript date object, defaults to null)
27773      */
27774     maxDate : null,
27775     /**
27776      * @cfg {String} minText
27777      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27778      */
27779     minText : "This date is before the minimum date",
27780     /**
27781      * @cfg {String} maxText
27782      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27783      */
27784     maxText : "This date is after the maximum date",
27785     /**
27786      * @cfg {String} format
27787      * The default date format string which can be overriden for localization support.  The format must be
27788      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27789      */
27790     format : "m/d/y",
27791     /**
27792      * @cfg {Array} disabledDays
27793      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27794      */
27795     disabledDays : null,
27796     /**
27797      * @cfg {String} disabledDaysText
27798      * The tooltip to display when the date falls on a disabled day (defaults to "")
27799      */
27800     disabledDaysText : "",
27801     /**
27802      * @cfg {RegExp} disabledDatesRE
27803      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27804      */
27805     disabledDatesRE : null,
27806     /**
27807      * @cfg {String} disabledDatesText
27808      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27809      */
27810     disabledDatesText : "",
27811     /**
27812      * @cfg {Boolean} constrainToViewport
27813      * True to constrain the date picker to the viewport (defaults to true)
27814      */
27815     constrainToViewport : true,
27816     /**
27817      * @cfg {Array} monthNames
27818      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27819      */
27820     monthNames : Date.monthNames,
27821     /**
27822      * @cfg {Array} dayNames
27823      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27824      */
27825     dayNames : Date.dayNames,
27826     /**
27827      * @cfg {String} nextText
27828      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27829      */
27830     nextText: 'Next Month (Control+Right)',
27831     /**
27832      * @cfg {String} prevText
27833      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27834      */
27835     prevText: 'Previous Month (Control+Left)',
27836     /**
27837      * @cfg {String} monthYearText
27838      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27839      */
27840     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27841     /**
27842      * @cfg {Number} startDay
27843      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27844      */
27845     startDay : 0,
27846     /**
27847      * @cfg {Bool} showClear
27848      * Show a clear button (usefull for date form elements that can be blank.)
27849      */
27850     
27851     showClear: false,
27852     
27853     /**
27854      * Sets the value of the date field
27855      * @param {Date} value The date to set
27856      */
27857     setValue : function(value){
27858         var old = this.value;
27859         
27860         if (typeof(value) == 'string') {
27861          
27862             value = Date.parseDate(value, this.format);
27863         }
27864         if (!value) {
27865             value = new Date();
27866         }
27867         
27868         this.value = value.clearTime(true);
27869         if(this.el){
27870             this.update(this.value);
27871         }
27872     },
27873
27874     /**
27875      * Gets the current selected value of the date field
27876      * @return {Date} The selected date
27877      */
27878     getValue : function(){
27879         return this.value;
27880     },
27881
27882     // private
27883     focus : function(){
27884         if(this.el){
27885             this.update(this.activeDate);
27886         }
27887     },
27888
27889     // privateval
27890     onRender : function(container, position){
27891         
27892         var m = [
27893              '<table cellspacing="0">',
27894                 '<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>',
27895                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27896         var dn = this.dayNames;
27897         for(var i = 0; i < 7; i++){
27898             var d = this.startDay+i;
27899             if(d > 6){
27900                 d = d-7;
27901             }
27902             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27903         }
27904         m[m.length] = "</tr></thead><tbody><tr>";
27905         for(var i = 0; i < 42; i++) {
27906             if(i % 7 == 0 && i != 0){
27907                 m[m.length] = "</tr><tr>";
27908             }
27909             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27910         }
27911         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27912             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27913
27914         var el = document.createElement("div");
27915         el.className = "x-date-picker";
27916         el.innerHTML = m.join("");
27917
27918         container.dom.insertBefore(el, position);
27919
27920         this.el = Roo.get(el);
27921         this.eventEl = Roo.get(el.firstChild);
27922
27923         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27924             handler: this.showPrevMonth,
27925             scope: this,
27926             preventDefault:true,
27927             stopDefault:true
27928         });
27929
27930         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27931             handler: this.showNextMonth,
27932             scope: this,
27933             preventDefault:true,
27934             stopDefault:true
27935         });
27936
27937         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27938
27939         this.monthPicker = this.el.down('div.x-date-mp');
27940         this.monthPicker.enableDisplayMode('block');
27941         
27942         var kn = new Roo.KeyNav(this.eventEl, {
27943             "left" : function(e){
27944                 e.ctrlKey ?
27945                     this.showPrevMonth() :
27946                     this.update(this.activeDate.add("d", -1));
27947             },
27948
27949             "right" : function(e){
27950                 e.ctrlKey ?
27951                     this.showNextMonth() :
27952                     this.update(this.activeDate.add("d", 1));
27953             },
27954
27955             "up" : function(e){
27956                 e.ctrlKey ?
27957                     this.showNextYear() :
27958                     this.update(this.activeDate.add("d", -7));
27959             },
27960
27961             "down" : function(e){
27962                 e.ctrlKey ?
27963                     this.showPrevYear() :
27964                     this.update(this.activeDate.add("d", 7));
27965             },
27966
27967             "pageUp" : function(e){
27968                 this.showNextMonth();
27969             },
27970
27971             "pageDown" : function(e){
27972                 this.showPrevMonth();
27973             },
27974
27975             "enter" : function(e){
27976                 e.stopPropagation();
27977                 return true;
27978             },
27979
27980             scope : this
27981         });
27982
27983         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27984
27985         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27986
27987         this.el.unselectable();
27988         
27989         this.cells = this.el.select("table.x-date-inner tbody td");
27990         this.textNodes = this.el.query("table.x-date-inner tbody span");
27991
27992         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27993             text: "&#160;",
27994             tooltip: this.monthYearText
27995         });
27996
27997         this.mbtn.on('click', this.showMonthPicker, this);
27998         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27999
28000
28001         var today = (new Date()).dateFormat(this.format);
28002         
28003         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28004         if (this.showClear) {
28005             baseTb.add( new Roo.Toolbar.Fill());
28006         }
28007         baseTb.add({
28008             text: String.format(this.todayText, today),
28009             tooltip: String.format(this.todayTip, today),
28010             handler: this.selectToday,
28011             scope: this
28012         });
28013         
28014         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28015             
28016         //});
28017         if (this.showClear) {
28018             
28019             baseTb.add( new Roo.Toolbar.Fill());
28020             baseTb.add({
28021                 text: '&#160;',
28022                 cls: 'x-btn-icon x-btn-clear',
28023                 handler: function() {
28024                     //this.value = '';
28025                     this.fireEvent("select", this, '');
28026                 },
28027                 scope: this
28028             });
28029         }
28030         
28031         
28032         if(Roo.isIE){
28033             this.el.repaint();
28034         }
28035         this.update(this.value);
28036     },
28037
28038     createMonthPicker : function(){
28039         if(!this.monthPicker.dom.firstChild){
28040             var buf = ['<table border="0" cellspacing="0">'];
28041             for(var i = 0; i < 6; i++){
28042                 buf.push(
28043                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28044                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28045                     i == 0 ?
28046                     '<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>' :
28047                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28048                 );
28049             }
28050             buf.push(
28051                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28052                     this.okText,
28053                     '</button><button type="button" class="x-date-mp-cancel">',
28054                     this.cancelText,
28055                     '</button></td></tr>',
28056                 '</table>'
28057             );
28058             this.monthPicker.update(buf.join(''));
28059             this.monthPicker.on('click', this.onMonthClick, this);
28060             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28061
28062             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28063             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28064
28065             this.mpMonths.each(function(m, a, i){
28066                 i += 1;
28067                 if((i%2) == 0){
28068                     m.dom.xmonth = 5 + Math.round(i * .5);
28069                 }else{
28070                     m.dom.xmonth = Math.round((i-1) * .5);
28071                 }
28072             });
28073         }
28074     },
28075
28076     showMonthPicker : function(){
28077         this.createMonthPicker();
28078         var size = this.el.getSize();
28079         this.monthPicker.setSize(size);
28080         this.monthPicker.child('table').setSize(size);
28081
28082         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28083         this.updateMPMonth(this.mpSelMonth);
28084         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28085         this.updateMPYear(this.mpSelYear);
28086
28087         this.monthPicker.slideIn('t', {duration:.2});
28088     },
28089
28090     updateMPYear : function(y){
28091         this.mpyear = y;
28092         var ys = this.mpYears.elements;
28093         for(var i = 1; i <= 10; i++){
28094             var td = ys[i-1], y2;
28095             if((i%2) == 0){
28096                 y2 = y + Math.round(i * .5);
28097                 td.firstChild.innerHTML = y2;
28098                 td.xyear = y2;
28099             }else{
28100                 y2 = y - (5-Math.round(i * .5));
28101                 td.firstChild.innerHTML = y2;
28102                 td.xyear = y2;
28103             }
28104             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28105         }
28106     },
28107
28108     updateMPMonth : function(sm){
28109         this.mpMonths.each(function(m, a, i){
28110             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28111         });
28112     },
28113
28114     selectMPMonth: function(m){
28115         
28116     },
28117
28118     onMonthClick : function(e, t){
28119         e.stopEvent();
28120         var el = new Roo.Element(t), pn;
28121         if(el.is('button.x-date-mp-cancel')){
28122             this.hideMonthPicker();
28123         }
28124         else if(el.is('button.x-date-mp-ok')){
28125             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28126             this.hideMonthPicker();
28127         }
28128         else if(pn = el.up('td.x-date-mp-month', 2)){
28129             this.mpMonths.removeClass('x-date-mp-sel');
28130             pn.addClass('x-date-mp-sel');
28131             this.mpSelMonth = pn.dom.xmonth;
28132         }
28133         else if(pn = el.up('td.x-date-mp-year', 2)){
28134             this.mpYears.removeClass('x-date-mp-sel');
28135             pn.addClass('x-date-mp-sel');
28136             this.mpSelYear = pn.dom.xyear;
28137         }
28138         else if(el.is('a.x-date-mp-prev')){
28139             this.updateMPYear(this.mpyear-10);
28140         }
28141         else if(el.is('a.x-date-mp-next')){
28142             this.updateMPYear(this.mpyear+10);
28143         }
28144     },
28145
28146     onMonthDblClick : function(e, t){
28147         e.stopEvent();
28148         var el = new Roo.Element(t), pn;
28149         if(pn = el.up('td.x-date-mp-month', 2)){
28150             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28151             this.hideMonthPicker();
28152         }
28153         else if(pn = el.up('td.x-date-mp-year', 2)){
28154             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28155             this.hideMonthPicker();
28156         }
28157     },
28158
28159     hideMonthPicker : function(disableAnim){
28160         if(this.monthPicker){
28161             if(disableAnim === true){
28162                 this.monthPicker.hide();
28163             }else{
28164                 this.monthPicker.slideOut('t', {duration:.2});
28165             }
28166         }
28167     },
28168
28169     // private
28170     showPrevMonth : function(e){
28171         this.update(this.activeDate.add("mo", -1));
28172     },
28173
28174     // private
28175     showNextMonth : function(e){
28176         this.update(this.activeDate.add("mo", 1));
28177     },
28178
28179     // private
28180     showPrevYear : function(){
28181         this.update(this.activeDate.add("y", -1));
28182     },
28183
28184     // private
28185     showNextYear : function(){
28186         this.update(this.activeDate.add("y", 1));
28187     },
28188
28189     // private
28190     handleMouseWheel : function(e){
28191         var delta = e.getWheelDelta();
28192         if(delta > 0){
28193             this.showPrevMonth();
28194             e.stopEvent();
28195         } else if(delta < 0){
28196             this.showNextMonth();
28197             e.stopEvent();
28198         }
28199     },
28200
28201     // private
28202     handleDateClick : function(e, t){
28203         e.stopEvent();
28204         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28205             this.setValue(new Date(t.dateValue));
28206             this.fireEvent("select", this, this.value);
28207         }
28208     },
28209
28210     // private
28211     selectToday : function(){
28212         this.setValue(new Date().clearTime());
28213         this.fireEvent("select", this, this.value);
28214     },
28215
28216     // private
28217     update : function(date)
28218     {
28219         var vd = this.activeDate;
28220         this.activeDate = date;
28221         if(vd && this.el){
28222             var t = date.getTime();
28223             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28224                 this.cells.removeClass("x-date-selected");
28225                 this.cells.each(function(c){
28226                    if(c.dom.firstChild.dateValue == t){
28227                        c.addClass("x-date-selected");
28228                        setTimeout(function(){
28229                             try{c.dom.firstChild.focus();}catch(e){}
28230                        }, 50);
28231                        return false;
28232                    }
28233                 });
28234                 return;
28235             }
28236         }
28237         
28238         var days = date.getDaysInMonth();
28239         var firstOfMonth = date.getFirstDateOfMonth();
28240         var startingPos = firstOfMonth.getDay()-this.startDay;
28241
28242         if(startingPos <= this.startDay){
28243             startingPos += 7;
28244         }
28245
28246         var pm = date.add("mo", -1);
28247         var prevStart = pm.getDaysInMonth()-startingPos;
28248
28249         var cells = this.cells.elements;
28250         var textEls = this.textNodes;
28251         days += startingPos;
28252
28253         // convert everything to numbers so it's fast
28254         var day = 86400000;
28255         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28256         var today = new Date().clearTime().getTime();
28257         var sel = date.clearTime().getTime();
28258         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28259         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28260         var ddMatch = this.disabledDatesRE;
28261         var ddText = this.disabledDatesText;
28262         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28263         var ddaysText = this.disabledDaysText;
28264         var format = this.format;
28265
28266         var setCellClass = function(cal, cell){
28267             cell.title = "";
28268             var t = d.getTime();
28269             cell.firstChild.dateValue = t;
28270             if(t == today){
28271                 cell.className += " x-date-today";
28272                 cell.title = cal.todayText;
28273             }
28274             if(t == sel){
28275                 cell.className += " x-date-selected";
28276                 setTimeout(function(){
28277                     try{cell.firstChild.focus();}catch(e){}
28278                 }, 50);
28279             }
28280             // disabling
28281             if(t < min) {
28282                 cell.className = " x-date-disabled";
28283                 cell.title = cal.minText;
28284                 return;
28285             }
28286             if(t > max) {
28287                 cell.className = " x-date-disabled";
28288                 cell.title = cal.maxText;
28289                 return;
28290             }
28291             if(ddays){
28292                 if(ddays.indexOf(d.getDay()) != -1){
28293                     cell.title = ddaysText;
28294                     cell.className = " x-date-disabled";
28295                 }
28296             }
28297             if(ddMatch && format){
28298                 var fvalue = d.dateFormat(format);
28299                 if(ddMatch.test(fvalue)){
28300                     cell.title = ddText.replace("%0", fvalue);
28301                     cell.className = " x-date-disabled";
28302                 }
28303             }
28304         };
28305
28306         var i = 0;
28307         for(; i < startingPos; i++) {
28308             textEls[i].innerHTML = (++prevStart);
28309             d.setDate(d.getDate()+1);
28310             cells[i].className = "x-date-prevday";
28311             setCellClass(this, cells[i]);
28312         }
28313         for(; i < days; i++){
28314             intDay = i - startingPos + 1;
28315             textEls[i].innerHTML = (intDay);
28316             d.setDate(d.getDate()+1);
28317             cells[i].className = "x-date-active";
28318             setCellClass(this, cells[i]);
28319         }
28320         var extraDays = 0;
28321         for(; i < 42; i++) {
28322              textEls[i].innerHTML = (++extraDays);
28323              d.setDate(d.getDate()+1);
28324              cells[i].className = "x-date-nextday";
28325              setCellClass(this, cells[i]);
28326         }
28327
28328         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28329         this.fireEvent('monthchange', this, date);
28330         
28331         if(!this.internalRender){
28332             var main = this.el.dom.firstChild;
28333             var w = main.offsetWidth;
28334             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28335             Roo.fly(main).setWidth(w);
28336             this.internalRender = true;
28337             // opera does not respect the auto grow header center column
28338             // then, after it gets a width opera refuses to recalculate
28339             // without a second pass
28340             if(Roo.isOpera && !this.secondPass){
28341                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28342                 this.secondPass = true;
28343                 this.update.defer(10, this, [date]);
28344             }
28345         }
28346         
28347         
28348     }
28349 });        /*
28350  * Based on:
28351  * Ext JS Library 1.1.1
28352  * Copyright(c) 2006-2007, Ext JS, LLC.
28353  *
28354  * Originally Released Under LGPL - original licence link has changed is not relivant.
28355  *
28356  * Fork - LGPL
28357  * <script type="text/javascript">
28358  */
28359 /**
28360  * @class Roo.TabPanel
28361  * @extends Roo.util.Observable
28362  * A lightweight tab container.
28363  * <br><br>
28364  * Usage:
28365  * <pre><code>
28366 // basic tabs 1, built from existing content
28367 var tabs = new Roo.TabPanel("tabs1");
28368 tabs.addTab("script", "View Script");
28369 tabs.addTab("markup", "View Markup");
28370 tabs.activate("script");
28371
28372 // more advanced tabs, built from javascript
28373 var jtabs = new Roo.TabPanel("jtabs");
28374 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28375
28376 // set up the UpdateManager
28377 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28378 var updater = tab2.getUpdateManager();
28379 updater.setDefaultUrl("ajax1.htm");
28380 tab2.on('activate', updater.refresh, updater, true);
28381
28382 // Use setUrl for Ajax loading
28383 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28384 tab3.setUrl("ajax2.htm", null, true);
28385
28386 // Disabled tab
28387 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28388 tab4.disable();
28389
28390 jtabs.activate("jtabs-1");
28391  * </code></pre>
28392  * @constructor
28393  * Create a new TabPanel.
28394  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28395  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28396  */
28397 Roo.TabPanel = function(container, config){
28398     /**
28399     * The container element for this TabPanel.
28400     * @type Roo.Element
28401     */
28402     this.el = Roo.get(container, true);
28403     if(config){
28404         if(typeof config == "boolean"){
28405             this.tabPosition = config ? "bottom" : "top";
28406         }else{
28407             Roo.apply(this, config);
28408         }
28409     }
28410     if(this.tabPosition == "bottom"){
28411         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28412         this.el.addClass("x-tabs-bottom");
28413     }
28414     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28415     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28416     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28417     if(Roo.isIE){
28418         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28419     }
28420     if(this.tabPosition != "bottom"){
28421         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28422          * @type Roo.Element
28423          */
28424         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28425         this.el.addClass("x-tabs-top");
28426     }
28427     this.items = [];
28428
28429     this.bodyEl.setStyle("position", "relative");
28430
28431     this.active = null;
28432     this.activateDelegate = this.activate.createDelegate(this);
28433
28434     this.addEvents({
28435         /**
28436          * @event tabchange
28437          * Fires when the active tab changes
28438          * @param {Roo.TabPanel} this
28439          * @param {Roo.TabPanelItem} activePanel The new active tab
28440          */
28441         "tabchange": true,
28442         /**
28443          * @event beforetabchange
28444          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28445          * @param {Roo.TabPanel} this
28446          * @param {Object} e Set cancel to true on this object to cancel the tab change
28447          * @param {Roo.TabPanelItem} tab The tab being changed to
28448          */
28449         "beforetabchange" : true
28450     });
28451
28452     Roo.EventManager.onWindowResize(this.onResize, this);
28453     this.cpad = this.el.getPadding("lr");
28454     this.hiddenCount = 0;
28455
28456
28457     // toolbar on the tabbar support...
28458     if (this.toolbar) {
28459         var tcfg = this.toolbar;
28460         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28461         this.toolbar = new Roo.Toolbar(tcfg);
28462         if (Roo.isSafari) {
28463             var tbl = tcfg.container.child('table', true);
28464             tbl.setAttribute('width', '100%');
28465         }
28466         
28467     }
28468    
28469
28470
28471     Roo.TabPanel.superclass.constructor.call(this);
28472 };
28473
28474 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28475     /*
28476      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28477      */
28478     tabPosition : "top",
28479     /*
28480      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28481      */
28482     currentTabWidth : 0,
28483     /*
28484      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28485      */
28486     minTabWidth : 40,
28487     /*
28488      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28489      */
28490     maxTabWidth : 250,
28491     /*
28492      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28493      */
28494     preferredTabWidth : 175,
28495     /*
28496      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28497      */
28498     resizeTabs : false,
28499     /*
28500      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28501      */
28502     monitorResize : true,
28503     /*
28504      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28505      */
28506     toolbar : false,
28507
28508     /**
28509      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28510      * @param {String} id The id of the div to use <b>or create</b>
28511      * @param {String} text The text for the tab
28512      * @param {String} content (optional) Content to put in the TabPanelItem body
28513      * @param {Boolean} closable (optional) True to create a close icon on the tab
28514      * @return {Roo.TabPanelItem} The created TabPanelItem
28515      */
28516     addTab : function(id, text, content, closable){
28517         var item = new Roo.TabPanelItem(this, id, text, closable);
28518         this.addTabItem(item);
28519         if(content){
28520             item.setContent(content);
28521         }
28522         return item;
28523     },
28524
28525     /**
28526      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28527      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28528      * @return {Roo.TabPanelItem}
28529      */
28530     getTab : function(id){
28531         return this.items[id];
28532     },
28533
28534     /**
28535      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28536      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28537      */
28538     hideTab : function(id){
28539         var t = this.items[id];
28540         if(!t.isHidden()){
28541            t.setHidden(true);
28542            this.hiddenCount++;
28543            this.autoSizeTabs();
28544         }
28545     },
28546
28547     /**
28548      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28549      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28550      */
28551     unhideTab : function(id){
28552         var t = this.items[id];
28553         if(t.isHidden()){
28554            t.setHidden(false);
28555            this.hiddenCount--;
28556            this.autoSizeTabs();
28557         }
28558     },
28559
28560     /**
28561      * Adds an existing {@link Roo.TabPanelItem}.
28562      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28563      */
28564     addTabItem : function(item){
28565         this.items[item.id] = item;
28566         this.items.push(item);
28567         if(this.resizeTabs){
28568            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28569            this.autoSizeTabs();
28570         }else{
28571             item.autoSize();
28572         }
28573     },
28574
28575     /**
28576      * Removes a {@link Roo.TabPanelItem}.
28577      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28578      */
28579     removeTab : function(id){
28580         var items = this.items;
28581         var tab = items[id];
28582         if(!tab) { return; }
28583         var index = items.indexOf(tab);
28584         if(this.active == tab && items.length > 1){
28585             var newTab = this.getNextAvailable(index);
28586             if(newTab) {
28587                 newTab.activate();
28588             }
28589         }
28590         this.stripEl.dom.removeChild(tab.pnode.dom);
28591         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28592             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28593         }
28594         items.splice(index, 1);
28595         delete this.items[tab.id];
28596         tab.fireEvent("close", tab);
28597         tab.purgeListeners();
28598         this.autoSizeTabs();
28599     },
28600
28601     getNextAvailable : function(start){
28602         var items = this.items;
28603         var index = start;
28604         // look for a next tab that will slide over to
28605         // replace the one being removed
28606         while(index < items.length){
28607             var item = items[++index];
28608             if(item && !item.isHidden()){
28609                 return item;
28610             }
28611         }
28612         // if one isn't found select the previous tab (on the left)
28613         index = start;
28614         while(index >= 0){
28615             var item = items[--index];
28616             if(item && !item.isHidden()){
28617                 return item;
28618             }
28619         }
28620         return null;
28621     },
28622
28623     /**
28624      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28625      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28626      */
28627     disableTab : function(id){
28628         var tab = this.items[id];
28629         if(tab && this.active != tab){
28630             tab.disable();
28631         }
28632     },
28633
28634     /**
28635      * Enables a {@link Roo.TabPanelItem} that is disabled.
28636      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28637      */
28638     enableTab : function(id){
28639         var tab = this.items[id];
28640         tab.enable();
28641     },
28642
28643     /**
28644      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28645      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28646      * @return {Roo.TabPanelItem} The TabPanelItem.
28647      */
28648     activate : function(id){
28649         var tab = this.items[id];
28650         if(!tab){
28651             return null;
28652         }
28653         if(tab == this.active || tab.disabled){
28654             return tab;
28655         }
28656         var e = {};
28657         this.fireEvent("beforetabchange", this, e, tab);
28658         if(e.cancel !== true && !tab.disabled){
28659             if(this.active){
28660                 this.active.hide();
28661             }
28662             this.active = this.items[id];
28663             this.active.show();
28664             this.fireEvent("tabchange", this, this.active);
28665         }
28666         return tab;
28667     },
28668
28669     /**
28670      * Gets the active {@link Roo.TabPanelItem}.
28671      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28672      */
28673     getActiveTab : function(){
28674         return this.active;
28675     },
28676
28677     /**
28678      * Updates the tab body element to fit the height of the container element
28679      * for overflow scrolling
28680      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28681      */
28682     syncHeight : function(targetHeight){
28683         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28684         var bm = this.bodyEl.getMargins();
28685         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28686         this.bodyEl.setHeight(newHeight);
28687         return newHeight;
28688     },
28689
28690     onResize : function(){
28691         if(this.monitorResize){
28692             this.autoSizeTabs();
28693         }
28694     },
28695
28696     /**
28697      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28698      */
28699     beginUpdate : function(){
28700         this.updating = true;
28701     },
28702
28703     /**
28704      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28705      */
28706     endUpdate : function(){
28707         this.updating = false;
28708         this.autoSizeTabs();
28709     },
28710
28711     /**
28712      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28713      */
28714     autoSizeTabs : function(){
28715         var count = this.items.length;
28716         var vcount = count - this.hiddenCount;
28717         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28718             return;
28719         }
28720         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28721         var availWidth = Math.floor(w / vcount);
28722         var b = this.stripBody;
28723         if(b.getWidth() > w){
28724             var tabs = this.items;
28725             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28726             if(availWidth < this.minTabWidth){
28727                 /*if(!this.sleft){    // incomplete scrolling code
28728                     this.createScrollButtons();
28729                 }
28730                 this.showScroll();
28731                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28732             }
28733         }else{
28734             if(this.currentTabWidth < this.preferredTabWidth){
28735                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28736             }
28737         }
28738     },
28739
28740     /**
28741      * Returns the number of tabs in this TabPanel.
28742      * @return {Number}
28743      */
28744      getCount : function(){
28745          return this.items.length;
28746      },
28747
28748     /**
28749      * Resizes all the tabs to the passed width
28750      * @param {Number} The new width
28751      */
28752     setTabWidth : function(width){
28753         this.currentTabWidth = width;
28754         for(var i = 0, len = this.items.length; i < len; i++) {
28755                 if(!this.items[i].isHidden()) {
28756                 this.items[i].setWidth(width);
28757             }
28758         }
28759     },
28760
28761     /**
28762      * Destroys this TabPanel
28763      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28764      */
28765     destroy : function(removeEl){
28766         Roo.EventManager.removeResizeListener(this.onResize, this);
28767         for(var i = 0, len = this.items.length; i < len; i++){
28768             this.items[i].purgeListeners();
28769         }
28770         if(removeEl === true){
28771             this.el.update("");
28772             this.el.remove();
28773         }
28774     }
28775 });
28776
28777 /**
28778  * @class Roo.TabPanelItem
28779  * @extends Roo.util.Observable
28780  * Represents an individual item (tab plus body) in a TabPanel.
28781  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28782  * @param {String} id The id of this TabPanelItem
28783  * @param {String} text The text for the tab of this TabPanelItem
28784  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28785  */
28786 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28787     /**
28788      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28789      * @type Roo.TabPanel
28790      */
28791     this.tabPanel = tabPanel;
28792     /**
28793      * The id for this TabPanelItem
28794      * @type String
28795      */
28796     this.id = id;
28797     /** @private */
28798     this.disabled = false;
28799     /** @private */
28800     this.text = text;
28801     /** @private */
28802     this.loaded = false;
28803     this.closable = closable;
28804
28805     /**
28806      * The body element for this TabPanelItem.
28807      * @type Roo.Element
28808      */
28809     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28810     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28811     this.bodyEl.setStyle("display", "block");
28812     this.bodyEl.setStyle("zoom", "1");
28813     this.hideAction();
28814
28815     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28816     /** @private */
28817     this.el = Roo.get(els.el, true);
28818     this.inner = Roo.get(els.inner, true);
28819     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28820     this.pnode = Roo.get(els.el.parentNode, true);
28821     this.el.on("mousedown", this.onTabMouseDown, this);
28822     this.el.on("click", this.onTabClick, this);
28823     /** @private */
28824     if(closable){
28825         var c = Roo.get(els.close, true);
28826         c.dom.title = this.closeText;
28827         c.addClassOnOver("close-over");
28828         c.on("click", this.closeClick, this);
28829      }
28830
28831     this.addEvents({
28832          /**
28833          * @event activate
28834          * Fires when this tab becomes the active tab.
28835          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28836          * @param {Roo.TabPanelItem} this
28837          */
28838         "activate": true,
28839         /**
28840          * @event beforeclose
28841          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28842          * @param {Roo.TabPanelItem} this
28843          * @param {Object} e Set cancel to true on this object to cancel the close.
28844          */
28845         "beforeclose": true,
28846         /**
28847          * @event close
28848          * Fires when this tab is closed.
28849          * @param {Roo.TabPanelItem} this
28850          */
28851          "close": true,
28852         /**
28853          * @event deactivate
28854          * Fires when this tab is no longer the active tab.
28855          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28856          * @param {Roo.TabPanelItem} this
28857          */
28858          "deactivate" : true
28859     });
28860     this.hidden = false;
28861
28862     Roo.TabPanelItem.superclass.constructor.call(this);
28863 };
28864
28865 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28866     purgeListeners : function(){
28867        Roo.util.Observable.prototype.purgeListeners.call(this);
28868        this.el.removeAllListeners();
28869     },
28870     /**
28871      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28872      */
28873     show : function(){
28874         this.pnode.addClass("on");
28875         this.showAction();
28876         if(Roo.isOpera){
28877             this.tabPanel.stripWrap.repaint();
28878         }
28879         this.fireEvent("activate", this.tabPanel, this);
28880     },
28881
28882     /**
28883      * Returns true if this tab is the active tab.
28884      * @return {Boolean}
28885      */
28886     isActive : function(){
28887         return this.tabPanel.getActiveTab() == this;
28888     },
28889
28890     /**
28891      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28892      */
28893     hide : function(){
28894         this.pnode.removeClass("on");
28895         this.hideAction();
28896         this.fireEvent("deactivate", this.tabPanel, this);
28897     },
28898
28899     hideAction : function(){
28900         this.bodyEl.hide();
28901         this.bodyEl.setStyle("position", "absolute");
28902         this.bodyEl.setLeft("-20000px");
28903         this.bodyEl.setTop("-20000px");
28904     },
28905
28906     showAction : function(){
28907         this.bodyEl.setStyle("position", "relative");
28908         this.bodyEl.setTop("");
28909         this.bodyEl.setLeft("");
28910         this.bodyEl.show();
28911     },
28912
28913     /**
28914      * Set the tooltip for the tab.
28915      * @param {String} tooltip The tab's tooltip
28916      */
28917     setTooltip : function(text){
28918         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28919             this.textEl.dom.qtip = text;
28920             this.textEl.dom.removeAttribute('title');
28921         }else{
28922             this.textEl.dom.title = text;
28923         }
28924     },
28925
28926     onTabClick : function(e){
28927         e.preventDefault();
28928         this.tabPanel.activate(this.id);
28929     },
28930
28931     onTabMouseDown : function(e){
28932         e.preventDefault();
28933         this.tabPanel.activate(this.id);
28934     },
28935
28936     getWidth : function(){
28937         return this.inner.getWidth();
28938     },
28939
28940     setWidth : function(width){
28941         var iwidth = width - this.pnode.getPadding("lr");
28942         this.inner.setWidth(iwidth);
28943         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28944         this.pnode.setWidth(width);
28945     },
28946
28947     /**
28948      * Show or hide the tab
28949      * @param {Boolean} hidden True to hide or false to show.
28950      */
28951     setHidden : function(hidden){
28952         this.hidden = hidden;
28953         this.pnode.setStyle("display", hidden ? "none" : "");
28954     },
28955
28956     /**
28957      * Returns true if this tab is "hidden"
28958      * @return {Boolean}
28959      */
28960     isHidden : function(){
28961         return this.hidden;
28962     },
28963
28964     /**
28965      * Returns the text for this tab
28966      * @return {String}
28967      */
28968     getText : function(){
28969         return this.text;
28970     },
28971
28972     autoSize : function(){
28973         //this.el.beginMeasure();
28974         this.textEl.setWidth(1);
28975         /*
28976          *  #2804 [new] Tabs in Roojs
28977          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28978          */
28979         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28980         //this.el.endMeasure();
28981     },
28982
28983     /**
28984      * Sets the text for the tab (Note: this also sets the tooltip text)
28985      * @param {String} text The tab's text and tooltip
28986      */
28987     setText : function(text){
28988         this.text = text;
28989         this.textEl.update(text);
28990         this.setTooltip(text);
28991         if(!this.tabPanel.resizeTabs){
28992             this.autoSize();
28993         }
28994     },
28995     /**
28996      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28997      */
28998     activate : function(){
28999         this.tabPanel.activate(this.id);
29000     },
29001
29002     /**
29003      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29004      */
29005     disable : function(){
29006         if(this.tabPanel.active != this){
29007             this.disabled = true;
29008             this.pnode.addClass("disabled");
29009         }
29010     },
29011
29012     /**
29013      * Enables this TabPanelItem if it was previously disabled.
29014      */
29015     enable : function(){
29016         this.disabled = false;
29017         this.pnode.removeClass("disabled");
29018     },
29019
29020     /**
29021      * Sets the content for this TabPanelItem.
29022      * @param {String} content The content
29023      * @param {Boolean} loadScripts true to look for and load scripts
29024      */
29025     setContent : function(content, loadScripts){
29026         this.bodyEl.update(content, loadScripts);
29027     },
29028
29029     /**
29030      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29031      * @return {Roo.UpdateManager} The UpdateManager
29032      */
29033     getUpdateManager : function(){
29034         return this.bodyEl.getUpdateManager();
29035     },
29036
29037     /**
29038      * Set a URL to be used to load the content for this TabPanelItem.
29039      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29040      * @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)
29041      * @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)
29042      * @return {Roo.UpdateManager} The UpdateManager
29043      */
29044     setUrl : function(url, params, loadOnce){
29045         if(this.refreshDelegate){
29046             this.un('activate', this.refreshDelegate);
29047         }
29048         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29049         this.on("activate", this.refreshDelegate);
29050         return this.bodyEl.getUpdateManager();
29051     },
29052
29053     /** @private */
29054     _handleRefresh : function(url, params, loadOnce){
29055         if(!loadOnce || !this.loaded){
29056             var updater = this.bodyEl.getUpdateManager();
29057             updater.update(url, params, this._setLoaded.createDelegate(this));
29058         }
29059     },
29060
29061     /**
29062      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29063      *   Will fail silently if the setUrl method has not been called.
29064      *   This does not activate the panel, just updates its content.
29065      */
29066     refresh : function(){
29067         if(this.refreshDelegate){
29068            this.loaded = false;
29069            this.refreshDelegate();
29070         }
29071     },
29072
29073     /** @private */
29074     _setLoaded : function(){
29075         this.loaded = true;
29076     },
29077
29078     /** @private */
29079     closeClick : function(e){
29080         var o = {};
29081         e.stopEvent();
29082         this.fireEvent("beforeclose", this, o);
29083         if(o.cancel !== true){
29084             this.tabPanel.removeTab(this.id);
29085         }
29086     },
29087     /**
29088      * The text displayed in the tooltip for the close icon.
29089      * @type String
29090      */
29091     closeText : "Close this tab"
29092 });
29093
29094 /** @private */
29095 Roo.TabPanel.prototype.createStrip = function(container){
29096     var strip = document.createElement("div");
29097     strip.className = "x-tabs-wrap";
29098     container.appendChild(strip);
29099     return strip;
29100 };
29101 /** @private */
29102 Roo.TabPanel.prototype.createStripList = function(strip){
29103     // div wrapper for retard IE
29104     // returns the "tr" element.
29105     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29106         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29107         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29108     return strip.firstChild.firstChild.firstChild.firstChild;
29109 };
29110 /** @private */
29111 Roo.TabPanel.prototype.createBody = function(container){
29112     var body = document.createElement("div");
29113     Roo.id(body, "tab-body");
29114     Roo.fly(body).addClass("x-tabs-body");
29115     container.appendChild(body);
29116     return body;
29117 };
29118 /** @private */
29119 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29120     var body = Roo.getDom(id);
29121     if(!body){
29122         body = document.createElement("div");
29123         body.id = id;
29124     }
29125     Roo.fly(body).addClass("x-tabs-item-body");
29126     bodyEl.insertBefore(body, bodyEl.firstChild);
29127     return body;
29128 };
29129 /** @private */
29130 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29131     var td = document.createElement("td");
29132     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29133     //stripEl.appendChild(td);
29134     if(closable){
29135         td.className = "x-tabs-closable";
29136         if(!this.closeTpl){
29137             this.closeTpl = new Roo.Template(
29138                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29139                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29140                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29141             );
29142         }
29143         var el = this.closeTpl.overwrite(td, {"text": text});
29144         var close = el.getElementsByTagName("div")[0];
29145         var inner = el.getElementsByTagName("em")[0];
29146         return {"el": el, "close": close, "inner": inner};
29147     } else {
29148         if(!this.tabTpl){
29149             this.tabTpl = new Roo.Template(
29150                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29151                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29152             );
29153         }
29154         var el = this.tabTpl.overwrite(td, {"text": text});
29155         var inner = el.getElementsByTagName("em")[0];
29156         return {"el": el, "inner": inner};
29157     }
29158 };/*
29159  * Based on:
29160  * Ext JS Library 1.1.1
29161  * Copyright(c) 2006-2007, Ext JS, LLC.
29162  *
29163  * Originally Released Under LGPL - original licence link has changed is not relivant.
29164  *
29165  * Fork - LGPL
29166  * <script type="text/javascript">
29167  */
29168
29169 /**
29170  * @class Roo.Button
29171  * @extends Roo.util.Observable
29172  * Simple Button class
29173  * @cfg {String} text The button text
29174  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29175  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29176  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29177  * @cfg {Object} scope The scope of the handler
29178  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29179  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29180  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29181  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29182  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29183  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29184    applies if enableToggle = true)
29185  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29186  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29187   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29188  * @constructor
29189  * Create a new button
29190  * @param {Object} config The config object
29191  */
29192 Roo.Button = function(renderTo, config)
29193 {
29194     if (!config) {
29195         config = renderTo;
29196         renderTo = config.renderTo || false;
29197     }
29198     
29199     Roo.apply(this, config);
29200     this.addEvents({
29201         /**
29202              * @event click
29203              * Fires when this button is clicked
29204              * @param {Button} this
29205              * @param {EventObject} e The click event
29206              */
29207             "click" : true,
29208         /**
29209              * @event toggle
29210              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29211              * @param {Button} this
29212              * @param {Boolean} pressed
29213              */
29214             "toggle" : true,
29215         /**
29216              * @event mouseover
29217              * Fires when the mouse hovers over the button
29218              * @param {Button} this
29219              * @param {Event} e The event object
29220              */
29221         'mouseover' : true,
29222         /**
29223              * @event mouseout
29224              * Fires when the mouse exits the button
29225              * @param {Button} this
29226              * @param {Event} e The event object
29227              */
29228         'mouseout': true,
29229          /**
29230              * @event render
29231              * Fires when the button is rendered
29232              * @param {Button} this
29233              */
29234         'render': true
29235     });
29236     if(this.menu){
29237         this.menu = Roo.menu.MenuMgr.get(this.menu);
29238     }
29239     // register listeners first!!  - so render can be captured..
29240     Roo.util.Observable.call(this);
29241     if(renderTo){
29242         this.render(renderTo);
29243     }
29244     
29245   
29246 };
29247
29248 Roo.extend(Roo.Button, Roo.util.Observable, {
29249     /**
29250      * 
29251      */
29252     
29253     /**
29254      * Read-only. True if this button is hidden
29255      * @type Boolean
29256      */
29257     hidden : false,
29258     /**
29259      * Read-only. True if this button is disabled
29260      * @type Boolean
29261      */
29262     disabled : false,
29263     /**
29264      * Read-only. True if this button is pressed (only if enableToggle = true)
29265      * @type Boolean
29266      */
29267     pressed : false,
29268
29269     /**
29270      * @cfg {Number} tabIndex 
29271      * The DOM tabIndex for this button (defaults to undefined)
29272      */
29273     tabIndex : undefined,
29274
29275     /**
29276      * @cfg {Boolean} enableToggle
29277      * True to enable pressed/not pressed toggling (defaults to false)
29278      */
29279     enableToggle: false,
29280     /**
29281      * @cfg {Mixed} menu
29282      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29283      */
29284     menu : undefined,
29285     /**
29286      * @cfg {String} menuAlign
29287      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29288      */
29289     menuAlign : "tl-bl?",
29290
29291     /**
29292      * @cfg {String} iconCls
29293      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29294      */
29295     iconCls : undefined,
29296     /**
29297      * @cfg {String} type
29298      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29299      */
29300     type : 'button',
29301
29302     // private
29303     menuClassTarget: 'tr',
29304
29305     /**
29306      * @cfg {String} clickEvent
29307      * The type of event to map to the button's event handler (defaults to 'click')
29308      */
29309     clickEvent : 'click',
29310
29311     /**
29312      * @cfg {Boolean} handleMouseEvents
29313      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29314      */
29315     handleMouseEvents : true,
29316
29317     /**
29318      * @cfg {String} tooltipType
29319      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29320      */
29321     tooltipType : 'qtip',
29322
29323     /**
29324      * @cfg {String} cls
29325      * A CSS class to apply to the button's main element.
29326      */
29327     
29328     /**
29329      * @cfg {Roo.Template} template (Optional)
29330      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29331      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29332      * require code modifications if required elements (e.g. a button) aren't present.
29333      */
29334
29335     // private
29336     render : function(renderTo){
29337         var btn;
29338         if(this.hideParent){
29339             this.parentEl = Roo.get(renderTo);
29340         }
29341         if(!this.dhconfig){
29342             if(!this.template){
29343                 if(!Roo.Button.buttonTemplate){
29344                     // hideous table template
29345                     Roo.Button.buttonTemplate = new Roo.Template(
29346                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29347                         '<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>',
29348                         "</tr></tbody></table>");
29349                 }
29350                 this.template = Roo.Button.buttonTemplate;
29351             }
29352             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29353             var btnEl = btn.child("button:first");
29354             btnEl.on('focus', this.onFocus, this);
29355             btnEl.on('blur', this.onBlur, this);
29356             if(this.cls){
29357                 btn.addClass(this.cls);
29358             }
29359             if(this.icon){
29360                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29361             }
29362             if(this.iconCls){
29363                 btnEl.addClass(this.iconCls);
29364                 if(!this.cls){
29365                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29366                 }
29367             }
29368             if(this.tabIndex !== undefined){
29369                 btnEl.dom.tabIndex = this.tabIndex;
29370             }
29371             if(this.tooltip){
29372                 if(typeof this.tooltip == 'object'){
29373                     Roo.QuickTips.tips(Roo.apply({
29374                           target: btnEl.id
29375                     }, this.tooltip));
29376                 } else {
29377                     btnEl.dom[this.tooltipType] = this.tooltip;
29378                 }
29379             }
29380         }else{
29381             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29382         }
29383         this.el = btn;
29384         if(this.id){
29385             this.el.dom.id = this.el.id = this.id;
29386         }
29387         if(this.menu){
29388             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29389             this.menu.on("show", this.onMenuShow, this);
29390             this.menu.on("hide", this.onMenuHide, this);
29391         }
29392         btn.addClass("x-btn");
29393         if(Roo.isIE && !Roo.isIE7){
29394             this.autoWidth.defer(1, this);
29395         }else{
29396             this.autoWidth();
29397         }
29398         if(this.handleMouseEvents){
29399             btn.on("mouseover", this.onMouseOver, this);
29400             btn.on("mouseout", this.onMouseOut, this);
29401             btn.on("mousedown", this.onMouseDown, this);
29402         }
29403         btn.on(this.clickEvent, this.onClick, this);
29404         //btn.on("mouseup", this.onMouseUp, this);
29405         if(this.hidden){
29406             this.hide();
29407         }
29408         if(this.disabled){
29409             this.disable();
29410         }
29411         Roo.ButtonToggleMgr.register(this);
29412         if(this.pressed){
29413             this.el.addClass("x-btn-pressed");
29414         }
29415         if(this.repeat){
29416             var repeater = new Roo.util.ClickRepeater(btn,
29417                 typeof this.repeat == "object" ? this.repeat : {}
29418             );
29419             repeater.on("click", this.onClick,  this);
29420         }
29421         
29422         this.fireEvent('render', this);
29423         
29424     },
29425     /**
29426      * Returns the button's underlying element
29427      * @return {Roo.Element} The element
29428      */
29429     getEl : function(){
29430         return this.el;  
29431     },
29432     
29433     /**
29434      * Destroys this Button and removes any listeners.
29435      */
29436     destroy : function(){
29437         Roo.ButtonToggleMgr.unregister(this);
29438         this.el.removeAllListeners();
29439         this.purgeListeners();
29440         this.el.remove();
29441     },
29442
29443     // private
29444     autoWidth : function(){
29445         if(this.el){
29446             this.el.setWidth("auto");
29447             if(Roo.isIE7 && Roo.isStrict){
29448                 var ib = this.el.child('button');
29449                 if(ib && ib.getWidth() > 20){
29450                     ib.clip();
29451                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29452                 }
29453             }
29454             if(this.minWidth){
29455                 if(this.hidden){
29456                     this.el.beginMeasure();
29457                 }
29458                 if(this.el.getWidth() < this.minWidth){
29459                     this.el.setWidth(this.minWidth);
29460                 }
29461                 if(this.hidden){
29462                     this.el.endMeasure();
29463                 }
29464             }
29465         }
29466     },
29467
29468     /**
29469      * Assigns this button's click handler
29470      * @param {Function} handler The function to call when the button is clicked
29471      * @param {Object} scope (optional) Scope for the function passed in
29472      */
29473     setHandler : function(handler, scope){
29474         this.handler = handler;
29475         this.scope = scope;  
29476     },
29477     
29478     /**
29479      * Sets this button's text
29480      * @param {String} text The button text
29481      */
29482     setText : function(text){
29483         this.text = text;
29484         if(this.el){
29485             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29486         }
29487         this.autoWidth();
29488     },
29489     
29490     /**
29491      * Gets the text for this button
29492      * @return {String} The button text
29493      */
29494     getText : function(){
29495         return this.text;  
29496     },
29497     
29498     /**
29499      * Show this button
29500      */
29501     show: function(){
29502         this.hidden = false;
29503         if(this.el){
29504             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29505         }
29506     },
29507     
29508     /**
29509      * Hide this button
29510      */
29511     hide: function(){
29512         this.hidden = true;
29513         if(this.el){
29514             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29515         }
29516     },
29517     
29518     /**
29519      * Convenience function for boolean show/hide
29520      * @param {Boolean} visible True to show, false to hide
29521      */
29522     setVisible: function(visible){
29523         if(visible) {
29524             this.show();
29525         }else{
29526             this.hide();
29527         }
29528     },
29529     
29530     /**
29531      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29532      * @param {Boolean} state (optional) Force a particular state
29533      */
29534     toggle : function(state){
29535         state = state === undefined ? !this.pressed : state;
29536         if(state != this.pressed){
29537             if(state){
29538                 this.el.addClass("x-btn-pressed");
29539                 this.pressed = true;
29540                 this.fireEvent("toggle", this, true);
29541             }else{
29542                 this.el.removeClass("x-btn-pressed");
29543                 this.pressed = false;
29544                 this.fireEvent("toggle", this, false);
29545             }
29546             if(this.toggleHandler){
29547                 this.toggleHandler.call(this.scope || this, this, state);
29548             }
29549         }
29550     },
29551     
29552     /**
29553      * Focus the button
29554      */
29555     focus : function(){
29556         this.el.child('button:first').focus();
29557     },
29558     
29559     /**
29560      * Disable this button
29561      */
29562     disable : function(){
29563         if(this.el){
29564             this.el.addClass("x-btn-disabled");
29565         }
29566         this.disabled = true;
29567     },
29568     
29569     /**
29570      * Enable this button
29571      */
29572     enable : function(){
29573         if(this.el){
29574             this.el.removeClass("x-btn-disabled");
29575         }
29576         this.disabled = false;
29577     },
29578
29579     /**
29580      * Convenience function for boolean enable/disable
29581      * @param {Boolean} enabled True to enable, false to disable
29582      */
29583     setDisabled : function(v){
29584         this[v !== true ? "enable" : "disable"]();
29585     },
29586
29587     // private
29588     onClick : function(e)
29589     {
29590         if(e){
29591             e.preventDefault();
29592         }
29593         if(e.button != 0){
29594             return;
29595         }
29596         if(!this.disabled){
29597             if(this.enableToggle){
29598                 this.toggle();
29599             }
29600             if(this.menu && !this.menu.isVisible()){
29601                 this.menu.show(this.el, this.menuAlign);
29602             }
29603             this.fireEvent("click", this, e);
29604             if(this.handler){
29605                 this.el.removeClass("x-btn-over");
29606                 this.handler.call(this.scope || this, this, e);
29607             }
29608         }
29609     },
29610     // private
29611     onMouseOver : function(e){
29612         if(!this.disabled){
29613             this.el.addClass("x-btn-over");
29614             this.fireEvent('mouseover', this, e);
29615         }
29616     },
29617     // private
29618     onMouseOut : function(e){
29619         if(!e.within(this.el,  true)){
29620             this.el.removeClass("x-btn-over");
29621             this.fireEvent('mouseout', this, e);
29622         }
29623     },
29624     // private
29625     onFocus : function(e){
29626         if(!this.disabled){
29627             this.el.addClass("x-btn-focus");
29628         }
29629     },
29630     // private
29631     onBlur : function(e){
29632         this.el.removeClass("x-btn-focus");
29633     },
29634     // private
29635     onMouseDown : function(e){
29636         if(!this.disabled && e.button == 0){
29637             this.el.addClass("x-btn-click");
29638             Roo.get(document).on('mouseup', this.onMouseUp, this);
29639         }
29640     },
29641     // private
29642     onMouseUp : function(e){
29643         if(e.button == 0){
29644             this.el.removeClass("x-btn-click");
29645             Roo.get(document).un('mouseup', this.onMouseUp, this);
29646         }
29647     },
29648     // private
29649     onMenuShow : function(e){
29650         this.el.addClass("x-btn-menu-active");
29651     },
29652     // private
29653     onMenuHide : function(e){
29654         this.el.removeClass("x-btn-menu-active");
29655     }   
29656 });
29657
29658 // Private utility class used by Button
29659 Roo.ButtonToggleMgr = function(){
29660    var groups = {};
29661    
29662    function toggleGroup(btn, state){
29663        if(state){
29664            var g = groups[btn.toggleGroup];
29665            for(var i = 0, l = g.length; i < l; i++){
29666                if(g[i] != btn){
29667                    g[i].toggle(false);
29668                }
29669            }
29670        }
29671    }
29672    
29673    return {
29674        register : function(btn){
29675            if(!btn.toggleGroup){
29676                return;
29677            }
29678            var g = groups[btn.toggleGroup];
29679            if(!g){
29680                g = groups[btn.toggleGroup] = [];
29681            }
29682            g.push(btn);
29683            btn.on("toggle", toggleGroup);
29684        },
29685        
29686        unregister : function(btn){
29687            if(!btn.toggleGroup){
29688                return;
29689            }
29690            var g = groups[btn.toggleGroup];
29691            if(g){
29692                g.remove(btn);
29693                btn.un("toggle", toggleGroup);
29694            }
29695        }
29696    };
29697 }();/*
29698  * Based on:
29699  * Ext JS Library 1.1.1
29700  * Copyright(c) 2006-2007, Ext JS, LLC.
29701  *
29702  * Originally Released Under LGPL - original licence link has changed is not relivant.
29703  *
29704  * Fork - LGPL
29705  * <script type="text/javascript">
29706  */
29707  
29708 /**
29709  * @class Roo.SplitButton
29710  * @extends Roo.Button
29711  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29712  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29713  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29714  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29715  * @cfg {String} arrowTooltip The title attribute of the arrow
29716  * @constructor
29717  * Create a new menu button
29718  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29719  * @param {Object} config The config object
29720  */
29721 Roo.SplitButton = function(renderTo, config){
29722     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29723     /**
29724      * @event arrowclick
29725      * Fires when this button's arrow is clicked
29726      * @param {SplitButton} this
29727      * @param {EventObject} e The click event
29728      */
29729     this.addEvents({"arrowclick":true});
29730 };
29731
29732 Roo.extend(Roo.SplitButton, Roo.Button, {
29733     render : function(renderTo){
29734         // this is one sweet looking template!
29735         var tpl = new Roo.Template(
29736             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29737             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29738             '<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>',
29739             "</tbody></table></td><td>",
29740             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29741             '<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>',
29742             "</tbody></table></td></tr></table>"
29743         );
29744         var btn = tpl.append(renderTo, [this.text, this.type], true);
29745         var btnEl = btn.child("button");
29746         if(this.cls){
29747             btn.addClass(this.cls);
29748         }
29749         if(this.icon){
29750             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29751         }
29752         if(this.iconCls){
29753             btnEl.addClass(this.iconCls);
29754             if(!this.cls){
29755                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29756             }
29757         }
29758         this.el = btn;
29759         if(this.handleMouseEvents){
29760             btn.on("mouseover", this.onMouseOver, this);
29761             btn.on("mouseout", this.onMouseOut, this);
29762             btn.on("mousedown", this.onMouseDown, this);
29763             btn.on("mouseup", this.onMouseUp, this);
29764         }
29765         btn.on(this.clickEvent, this.onClick, this);
29766         if(this.tooltip){
29767             if(typeof this.tooltip == 'object'){
29768                 Roo.QuickTips.tips(Roo.apply({
29769                       target: btnEl.id
29770                 }, this.tooltip));
29771             } else {
29772                 btnEl.dom[this.tooltipType] = this.tooltip;
29773             }
29774         }
29775         if(this.arrowTooltip){
29776             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29777         }
29778         if(this.hidden){
29779             this.hide();
29780         }
29781         if(this.disabled){
29782             this.disable();
29783         }
29784         if(this.pressed){
29785             this.el.addClass("x-btn-pressed");
29786         }
29787         if(Roo.isIE && !Roo.isIE7){
29788             this.autoWidth.defer(1, this);
29789         }else{
29790             this.autoWidth();
29791         }
29792         if(this.menu){
29793             this.menu.on("show", this.onMenuShow, this);
29794             this.menu.on("hide", this.onMenuHide, this);
29795         }
29796         this.fireEvent('render', this);
29797     },
29798
29799     // private
29800     autoWidth : function(){
29801         if(this.el){
29802             var tbl = this.el.child("table:first");
29803             var tbl2 = this.el.child("table:last");
29804             this.el.setWidth("auto");
29805             tbl.setWidth("auto");
29806             if(Roo.isIE7 && Roo.isStrict){
29807                 var ib = this.el.child('button:first');
29808                 if(ib && ib.getWidth() > 20){
29809                     ib.clip();
29810                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29811                 }
29812             }
29813             if(this.minWidth){
29814                 if(this.hidden){
29815                     this.el.beginMeasure();
29816                 }
29817                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29818                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29819                 }
29820                 if(this.hidden){
29821                     this.el.endMeasure();
29822                 }
29823             }
29824             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29825         } 
29826     },
29827     /**
29828      * Sets this button's click handler
29829      * @param {Function} handler The function to call when the button is clicked
29830      * @param {Object} scope (optional) Scope for the function passed above
29831      */
29832     setHandler : function(handler, scope){
29833         this.handler = handler;
29834         this.scope = scope;  
29835     },
29836     
29837     /**
29838      * Sets this button's arrow click handler
29839      * @param {Function} handler The function to call when the arrow is clicked
29840      * @param {Object} scope (optional) Scope for the function passed above
29841      */
29842     setArrowHandler : function(handler, scope){
29843         this.arrowHandler = handler;
29844         this.scope = scope;  
29845     },
29846     
29847     /**
29848      * Focus the button
29849      */
29850     focus : function(){
29851         if(this.el){
29852             this.el.child("button:first").focus();
29853         }
29854     },
29855
29856     // private
29857     onClick : function(e){
29858         e.preventDefault();
29859         if(!this.disabled){
29860             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29861                 if(this.menu && !this.menu.isVisible()){
29862                     this.menu.show(this.el, this.menuAlign);
29863                 }
29864                 this.fireEvent("arrowclick", this, e);
29865                 if(this.arrowHandler){
29866                     this.arrowHandler.call(this.scope || this, this, e);
29867                 }
29868             }else{
29869                 this.fireEvent("click", this, e);
29870                 if(this.handler){
29871                     this.handler.call(this.scope || this, this, e);
29872                 }
29873             }
29874         }
29875     },
29876     // private
29877     onMouseDown : function(e){
29878         if(!this.disabled){
29879             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29880         }
29881     },
29882     // private
29883     onMouseUp : function(e){
29884         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29885     }   
29886 });
29887
29888
29889 // backwards compat
29890 Roo.MenuButton = Roo.SplitButton;/*
29891  * Based on:
29892  * Ext JS Library 1.1.1
29893  * Copyright(c) 2006-2007, Ext JS, LLC.
29894  *
29895  * Originally Released Under LGPL - original licence link has changed is not relivant.
29896  *
29897  * Fork - LGPL
29898  * <script type="text/javascript">
29899  */
29900
29901 /**
29902  * @class Roo.Toolbar
29903  * Basic Toolbar class.
29904  * @constructor
29905  * Creates a new Toolbar
29906  * @param {Object} container The config object
29907  */ 
29908 Roo.Toolbar = function(container, buttons, config)
29909 {
29910     /// old consturctor format still supported..
29911     if(container instanceof Array){ // omit the container for later rendering
29912         buttons = container;
29913         config = buttons;
29914         container = null;
29915     }
29916     if (typeof(container) == 'object' && container.xtype) {
29917         config = container;
29918         container = config.container;
29919         buttons = config.buttons || []; // not really - use items!!
29920     }
29921     var xitems = [];
29922     if (config && config.items) {
29923         xitems = config.items;
29924         delete config.items;
29925     }
29926     Roo.apply(this, config);
29927     this.buttons = buttons;
29928     
29929     if(container){
29930         this.render(container);
29931     }
29932     this.xitems = xitems;
29933     Roo.each(xitems, function(b) {
29934         this.add(b);
29935     }, this);
29936     
29937 };
29938
29939 Roo.Toolbar.prototype = {
29940     /**
29941      * @cfg {Array} items
29942      * array of button configs or elements to add (will be converted to a MixedCollection)
29943      */
29944     
29945     /**
29946      * @cfg {String/HTMLElement/Element} container
29947      * The id or element that will contain the toolbar
29948      */
29949     // private
29950     render : function(ct){
29951         this.el = Roo.get(ct);
29952         if(this.cls){
29953             this.el.addClass(this.cls);
29954         }
29955         // using a table allows for vertical alignment
29956         // 100% width is needed by Safari...
29957         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29958         this.tr = this.el.child("tr", true);
29959         var autoId = 0;
29960         this.items = new Roo.util.MixedCollection(false, function(o){
29961             return o.id || ("item" + (++autoId));
29962         });
29963         if(this.buttons){
29964             this.add.apply(this, this.buttons);
29965             delete this.buttons;
29966         }
29967     },
29968
29969     /**
29970      * Adds element(s) to the toolbar -- this function takes a variable number of 
29971      * arguments of mixed type and adds them to the toolbar.
29972      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29973      * <ul>
29974      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29975      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29976      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29977      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29978      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29979      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29980      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29981      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29982      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29983      * </ul>
29984      * @param {Mixed} arg2
29985      * @param {Mixed} etc.
29986      */
29987     add : function(){
29988         var a = arguments, l = a.length;
29989         for(var i = 0; i < l; i++){
29990             this._add(a[i]);
29991         }
29992     },
29993     // private..
29994     _add : function(el) {
29995         
29996         if (el.xtype) {
29997             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29998         }
29999         
30000         if (el.applyTo){ // some kind of form field
30001             return this.addField(el);
30002         } 
30003         if (el.render){ // some kind of Toolbar.Item
30004             return this.addItem(el);
30005         }
30006         if (typeof el == "string"){ // string
30007             if(el == "separator" || el == "-"){
30008                 return this.addSeparator();
30009             }
30010             if (el == " "){
30011                 return this.addSpacer();
30012             }
30013             if(el == "->"){
30014                 return this.addFill();
30015             }
30016             return this.addText(el);
30017             
30018         }
30019         if(el.tagName){ // element
30020             return this.addElement(el);
30021         }
30022         if(typeof el == "object"){ // must be button config?
30023             return this.addButton(el);
30024         }
30025         // and now what?!?!
30026         return false;
30027         
30028     },
30029     
30030     /**
30031      * Add an Xtype element
30032      * @param {Object} xtype Xtype Object
30033      * @return {Object} created Object
30034      */
30035     addxtype : function(e){
30036         return this.add(e);  
30037     },
30038     
30039     /**
30040      * Returns the Element for this toolbar.
30041      * @return {Roo.Element}
30042      */
30043     getEl : function(){
30044         return this.el;  
30045     },
30046     
30047     /**
30048      * Adds a separator
30049      * @return {Roo.Toolbar.Item} The separator item
30050      */
30051     addSeparator : function(){
30052         return this.addItem(new Roo.Toolbar.Separator());
30053     },
30054
30055     /**
30056      * Adds a spacer element
30057      * @return {Roo.Toolbar.Spacer} The spacer item
30058      */
30059     addSpacer : function(){
30060         return this.addItem(new Roo.Toolbar.Spacer());
30061     },
30062
30063     /**
30064      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30065      * @return {Roo.Toolbar.Fill} The fill item
30066      */
30067     addFill : function(){
30068         return this.addItem(new Roo.Toolbar.Fill());
30069     },
30070
30071     /**
30072      * Adds any standard HTML element to the toolbar
30073      * @param {String/HTMLElement/Element} el The element or id of the element to add
30074      * @return {Roo.Toolbar.Item} The element's item
30075      */
30076     addElement : function(el){
30077         return this.addItem(new Roo.Toolbar.Item(el));
30078     },
30079     /**
30080      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30081      * @type Roo.util.MixedCollection  
30082      */
30083     items : false,
30084      
30085     /**
30086      * Adds any Toolbar.Item or subclass
30087      * @param {Roo.Toolbar.Item} item
30088      * @return {Roo.Toolbar.Item} The item
30089      */
30090     addItem : function(item){
30091         var td = this.nextBlock();
30092         item.render(td);
30093         this.items.add(item);
30094         return item;
30095     },
30096     
30097     /**
30098      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30099      * @param {Object/Array} config A button config or array of configs
30100      * @return {Roo.Toolbar.Button/Array}
30101      */
30102     addButton : function(config){
30103         if(config instanceof Array){
30104             var buttons = [];
30105             for(var i = 0, len = config.length; i < len; i++) {
30106                 buttons.push(this.addButton(config[i]));
30107             }
30108             return buttons;
30109         }
30110         var b = config;
30111         if(!(config instanceof Roo.Toolbar.Button)){
30112             b = config.split ?
30113                 new Roo.Toolbar.SplitButton(config) :
30114                 new Roo.Toolbar.Button(config);
30115         }
30116         var td = this.nextBlock();
30117         b.render(td);
30118         this.items.add(b);
30119         return b;
30120     },
30121     
30122     /**
30123      * Adds text to the toolbar
30124      * @param {String} text The text to add
30125      * @return {Roo.Toolbar.Item} The element's item
30126      */
30127     addText : function(text){
30128         return this.addItem(new Roo.Toolbar.TextItem(text));
30129     },
30130     
30131     /**
30132      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30133      * @param {Number} index The index where the item is to be inserted
30134      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30135      * @return {Roo.Toolbar.Button/Item}
30136      */
30137     insertButton : function(index, item){
30138         if(item instanceof Array){
30139             var buttons = [];
30140             for(var i = 0, len = item.length; i < len; i++) {
30141                buttons.push(this.insertButton(index + i, item[i]));
30142             }
30143             return buttons;
30144         }
30145         if (!(item instanceof Roo.Toolbar.Button)){
30146            item = new Roo.Toolbar.Button(item);
30147         }
30148         var td = document.createElement("td");
30149         this.tr.insertBefore(td, this.tr.childNodes[index]);
30150         item.render(td);
30151         this.items.insert(index, item);
30152         return item;
30153     },
30154     
30155     /**
30156      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30157      * @param {Object} config
30158      * @return {Roo.Toolbar.Item} The element's item
30159      */
30160     addDom : function(config, returnEl){
30161         var td = this.nextBlock();
30162         Roo.DomHelper.overwrite(td, config);
30163         var ti = new Roo.Toolbar.Item(td.firstChild);
30164         ti.render(td);
30165         this.items.add(ti);
30166         return ti;
30167     },
30168
30169     /**
30170      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30171      * @type Roo.util.MixedCollection  
30172      */
30173     fields : false,
30174     
30175     /**
30176      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30177      * Note: the field should not have been rendered yet. For a field that has already been
30178      * rendered, use {@link #addElement}.
30179      * @param {Roo.form.Field} field
30180      * @return {Roo.ToolbarItem}
30181      */
30182      
30183       
30184     addField : function(field) {
30185         if (!this.fields) {
30186             var autoId = 0;
30187             this.fields = new Roo.util.MixedCollection(false, function(o){
30188                 return o.id || ("item" + (++autoId));
30189             });
30190
30191         }
30192         
30193         var td = this.nextBlock();
30194         field.render(td);
30195         var ti = new Roo.Toolbar.Item(td.firstChild);
30196         ti.render(td);
30197         this.items.add(ti);
30198         this.fields.add(field);
30199         return ti;
30200     },
30201     /**
30202      * Hide the toolbar
30203      * @method hide
30204      */
30205      
30206       
30207     hide : function()
30208     {
30209         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30210         this.el.child('div').hide();
30211     },
30212     /**
30213      * Show the toolbar
30214      * @method show
30215      */
30216     show : function()
30217     {
30218         this.el.child('div').show();
30219     },
30220       
30221     // private
30222     nextBlock : function(){
30223         var td = document.createElement("td");
30224         this.tr.appendChild(td);
30225         return td;
30226     },
30227
30228     // private
30229     destroy : function(){
30230         if(this.items){ // rendered?
30231             Roo.destroy.apply(Roo, this.items.items);
30232         }
30233         if(this.fields){ // rendered?
30234             Roo.destroy.apply(Roo, this.fields.items);
30235         }
30236         Roo.Element.uncache(this.el, this.tr);
30237     }
30238 };
30239
30240 /**
30241  * @class Roo.Toolbar.Item
30242  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30243  * @constructor
30244  * Creates a new Item
30245  * @param {HTMLElement} el 
30246  */
30247 Roo.Toolbar.Item = function(el){
30248     var cfg = {};
30249     if (typeof (el.xtype) != 'undefined') {
30250         cfg = el;
30251         el = cfg.el;
30252     }
30253     
30254     this.el = Roo.getDom(el);
30255     this.id = Roo.id(this.el);
30256     this.hidden = false;
30257     
30258     this.addEvents({
30259          /**
30260              * @event render
30261              * Fires when the button is rendered
30262              * @param {Button} this
30263              */
30264         'render': true
30265     });
30266     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30267 };
30268 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30269 //Roo.Toolbar.Item.prototype = {
30270     
30271     /**
30272      * Get this item's HTML Element
30273      * @return {HTMLElement}
30274      */
30275     getEl : function(){
30276        return this.el;  
30277     },
30278
30279     // private
30280     render : function(td){
30281         
30282          this.td = td;
30283         td.appendChild(this.el);
30284         
30285         this.fireEvent('render', this);
30286     },
30287     
30288     /**
30289      * Removes and destroys this item.
30290      */
30291     destroy : function(){
30292         this.td.parentNode.removeChild(this.td);
30293     },
30294     
30295     /**
30296      * Shows this item.
30297      */
30298     show: function(){
30299         this.hidden = false;
30300         this.td.style.display = "";
30301     },
30302     
30303     /**
30304      * Hides this item.
30305      */
30306     hide: function(){
30307         this.hidden = true;
30308         this.td.style.display = "none";
30309     },
30310     
30311     /**
30312      * Convenience function for boolean show/hide.
30313      * @param {Boolean} visible true to show/false to hide
30314      */
30315     setVisible: function(visible){
30316         if(visible) {
30317             this.show();
30318         }else{
30319             this.hide();
30320         }
30321     },
30322     
30323     /**
30324      * Try to focus this item.
30325      */
30326     focus : function(){
30327         Roo.fly(this.el).focus();
30328     },
30329     
30330     /**
30331      * Disables this item.
30332      */
30333     disable : function(){
30334         Roo.fly(this.td).addClass("x-item-disabled");
30335         this.disabled = true;
30336         this.el.disabled = true;
30337     },
30338     
30339     /**
30340      * Enables this item.
30341      */
30342     enable : function(){
30343         Roo.fly(this.td).removeClass("x-item-disabled");
30344         this.disabled = false;
30345         this.el.disabled = false;
30346     }
30347 });
30348
30349
30350 /**
30351  * @class Roo.Toolbar.Separator
30352  * @extends Roo.Toolbar.Item
30353  * A simple toolbar separator class
30354  * @constructor
30355  * Creates a new Separator
30356  */
30357 Roo.Toolbar.Separator = function(cfg){
30358     
30359     var s = document.createElement("span");
30360     s.className = "ytb-sep";
30361     if (cfg) {
30362         cfg.el = s;
30363     }
30364     
30365     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30366 };
30367 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30368     enable:Roo.emptyFn,
30369     disable:Roo.emptyFn,
30370     focus:Roo.emptyFn
30371 });
30372
30373 /**
30374  * @class Roo.Toolbar.Spacer
30375  * @extends Roo.Toolbar.Item
30376  * A simple element that adds extra horizontal space to a toolbar.
30377  * @constructor
30378  * Creates a new Spacer
30379  */
30380 Roo.Toolbar.Spacer = function(cfg){
30381     var s = document.createElement("div");
30382     s.className = "ytb-spacer";
30383     if (cfg) {
30384         cfg.el = s;
30385     }
30386     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30387 };
30388 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30389     enable:Roo.emptyFn,
30390     disable:Roo.emptyFn,
30391     focus:Roo.emptyFn
30392 });
30393
30394 /**
30395  * @class Roo.Toolbar.Fill
30396  * @extends Roo.Toolbar.Spacer
30397  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30398  * @constructor
30399  * Creates a new Spacer
30400  */
30401 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30402     // private
30403     render : function(td){
30404         td.style.width = '100%';
30405         Roo.Toolbar.Fill.superclass.render.call(this, td);
30406     }
30407 });
30408
30409 /**
30410  * @class Roo.Toolbar.TextItem
30411  * @extends Roo.Toolbar.Item
30412  * A simple class that renders text directly into a toolbar.
30413  * @constructor
30414  * Creates a new TextItem
30415  * @param {String} text
30416  */
30417 Roo.Toolbar.TextItem = function(cfg){
30418     var  text = cfg || "";
30419     if (typeof(cfg) == 'object') {
30420         text = cfg.text || "";
30421     }  else {
30422         cfg = null;
30423     }
30424     var s = document.createElement("span");
30425     s.className = "ytb-text";
30426     s.innerHTML = text;
30427     if (cfg) {
30428         cfg.el  = s;
30429     }
30430     
30431     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30432 };
30433 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30434     
30435      
30436     enable:Roo.emptyFn,
30437     disable:Roo.emptyFn,
30438     focus:Roo.emptyFn
30439 });
30440
30441 /**
30442  * @class Roo.Toolbar.Button
30443  * @extends Roo.Button
30444  * A button that renders into a toolbar.
30445  * @constructor
30446  * Creates a new Button
30447  * @param {Object} config A standard {@link Roo.Button} config object
30448  */
30449 Roo.Toolbar.Button = function(config){
30450     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30451 };
30452 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30453     render : function(td){
30454         this.td = td;
30455         Roo.Toolbar.Button.superclass.render.call(this, td);
30456     },
30457     
30458     /**
30459      * Removes and destroys this button
30460      */
30461     destroy : function(){
30462         Roo.Toolbar.Button.superclass.destroy.call(this);
30463         this.td.parentNode.removeChild(this.td);
30464     },
30465     
30466     /**
30467      * Shows this button
30468      */
30469     show: function(){
30470         this.hidden = false;
30471         this.td.style.display = "";
30472     },
30473     
30474     /**
30475      * Hides this button
30476      */
30477     hide: function(){
30478         this.hidden = true;
30479         this.td.style.display = "none";
30480     },
30481
30482     /**
30483      * Disables this item
30484      */
30485     disable : function(){
30486         Roo.fly(this.td).addClass("x-item-disabled");
30487         this.disabled = true;
30488     },
30489
30490     /**
30491      * Enables this item
30492      */
30493     enable : function(){
30494         Roo.fly(this.td).removeClass("x-item-disabled");
30495         this.disabled = false;
30496     }
30497 });
30498 // backwards compat
30499 Roo.ToolbarButton = Roo.Toolbar.Button;
30500
30501 /**
30502  * @class Roo.Toolbar.SplitButton
30503  * @extends Roo.SplitButton
30504  * A menu button that renders into a toolbar.
30505  * @constructor
30506  * Creates a new SplitButton
30507  * @param {Object} config A standard {@link Roo.SplitButton} config object
30508  */
30509 Roo.Toolbar.SplitButton = function(config){
30510     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30511 };
30512 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30513     render : function(td){
30514         this.td = td;
30515         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30516     },
30517     
30518     /**
30519      * Removes and destroys this button
30520      */
30521     destroy : function(){
30522         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30523         this.td.parentNode.removeChild(this.td);
30524     },
30525     
30526     /**
30527      * Shows this button
30528      */
30529     show: function(){
30530         this.hidden = false;
30531         this.td.style.display = "";
30532     },
30533     
30534     /**
30535      * Hides this button
30536      */
30537     hide: function(){
30538         this.hidden = true;
30539         this.td.style.display = "none";
30540     }
30541 });
30542
30543 // backwards compat
30544 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30545  * Based on:
30546  * Ext JS Library 1.1.1
30547  * Copyright(c) 2006-2007, Ext JS, LLC.
30548  *
30549  * Originally Released Under LGPL - original licence link has changed is not relivant.
30550  *
30551  * Fork - LGPL
30552  * <script type="text/javascript">
30553  */
30554  
30555 /**
30556  * @class Roo.PagingToolbar
30557  * @extends Roo.Toolbar
30558  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30559  * @constructor
30560  * Create a new PagingToolbar
30561  * @param {Object} config The config object
30562  */
30563 Roo.PagingToolbar = function(el, ds, config)
30564 {
30565     // old args format still supported... - xtype is prefered..
30566     if (typeof(el) == 'object' && el.xtype) {
30567         // created from xtype...
30568         config = el;
30569         ds = el.dataSource;
30570         el = config.container;
30571     }
30572     var items = [];
30573     if (config.items) {
30574         items = config.items;
30575         config.items = [];
30576     }
30577     
30578     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30579     this.ds = ds;
30580     this.cursor = 0;
30581     this.renderButtons(this.el);
30582     this.bind(ds);
30583     
30584     // supprot items array.
30585    
30586     Roo.each(items, function(e) {
30587         this.add(Roo.factory(e));
30588     },this);
30589     
30590 };
30591
30592 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30593     /**
30594      * @cfg {Roo.data.Store} dataSource
30595      * The underlying data store providing the paged data
30596      */
30597     /**
30598      * @cfg {String/HTMLElement/Element} container
30599      * container The id or element that will contain the toolbar
30600      */
30601     /**
30602      * @cfg {Boolean} displayInfo
30603      * True to display the displayMsg (defaults to false)
30604      */
30605     /**
30606      * @cfg {Number} pageSize
30607      * The number of records to display per page (defaults to 20)
30608      */
30609     pageSize: 20,
30610     /**
30611      * @cfg {String} displayMsg
30612      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30613      */
30614     displayMsg : 'Displaying {0} - {1} of {2}',
30615     /**
30616      * @cfg {String} emptyMsg
30617      * The message to display when no records are found (defaults to "No data to display")
30618      */
30619     emptyMsg : 'No data to display',
30620     /**
30621      * Customizable piece of the default paging text (defaults to "Page")
30622      * @type String
30623      */
30624     beforePageText : "Page",
30625     /**
30626      * Customizable piece of the default paging text (defaults to "of %0")
30627      * @type String
30628      */
30629     afterPageText : "of {0}",
30630     /**
30631      * Customizable piece of the default paging text (defaults to "First Page")
30632      * @type String
30633      */
30634     firstText : "First Page",
30635     /**
30636      * Customizable piece of the default paging text (defaults to "Previous Page")
30637      * @type String
30638      */
30639     prevText : "Previous Page",
30640     /**
30641      * Customizable piece of the default paging text (defaults to "Next Page")
30642      * @type String
30643      */
30644     nextText : "Next Page",
30645     /**
30646      * Customizable piece of the default paging text (defaults to "Last Page")
30647      * @type String
30648      */
30649     lastText : "Last Page",
30650     /**
30651      * Customizable piece of the default paging text (defaults to "Refresh")
30652      * @type String
30653      */
30654     refreshText : "Refresh",
30655
30656     // private
30657     renderButtons : function(el){
30658         Roo.PagingToolbar.superclass.render.call(this, el);
30659         this.first = this.addButton({
30660             tooltip: this.firstText,
30661             cls: "x-btn-icon x-grid-page-first",
30662             disabled: true,
30663             handler: this.onClick.createDelegate(this, ["first"])
30664         });
30665         this.prev = this.addButton({
30666             tooltip: this.prevText,
30667             cls: "x-btn-icon x-grid-page-prev",
30668             disabled: true,
30669             handler: this.onClick.createDelegate(this, ["prev"])
30670         });
30671         //this.addSeparator();
30672         this.add(this.beforePageText);
30673         this.field = Roo.get(this.addDom({
30674            tag: "input",
30675            type: "text",
30676            size: "3",
30677            value: "1",
30678            cls: "x-grid-page-number"
30679         }).el);
30680         this.field.on("keydown", this.onPagingKeydown, this);
30681         this.field.on("focus", function(){this.dom.select();});
30682         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30683         this.field.setHeight(18);
30684         //this.addSeparator();
30685         this.next = this.addButton({
30686             tooltip: this.nextText,
30687             cls: "x-btn-icon x-grid-page-next",
30688             disabled: true,
30689             handler: this.onClick.createDelegate(this, ["next"])
30690         });
30691         this.last = this.addButton({
30692             tooltip: this.lastText,
30693             cls: "x-btn-icon x-grid-page-last",
30694             disabled: true,
30695             handler: this.onClick.createDelegate(this, ["last"])
30696         });
30697         //this.addSeparator();
30698         this.loading = this.addButton({
30699             tooltip: this.refreshText,
30700             cls: "x-btn-icon x-grid-loading",
30701             handler: this.onClick.createDelegate(this, ["refresh"])
30702         });
30703
30704         if(this.displayInfo){
30705             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30706         }
30707     },
30708
30709     // private
30710     updateInfo : function(){
30711         if(this.displayEl){
30712             var count = this.ds.getCount();
30713             var msg = count == 0 ?
30714                 this.emptyMsg :
30715                 String.format(
30716                     this.displayMsg,
30717                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30718                 );
30719             this.displayEl.update(msg);
30720         }
30721     },
30722
30723     // private
30724     onLoad : function(ds, r, o){
30725        this.cursor = o.params ? o.params.start : 0;
30726        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30727
30728        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30729        this.field.dom.value = ap;
30730        this.first.setDisabled(ap == 1);
30731        this.prev.setDisabled(ap == 1);
30732        this.next.setDisabled(ap == ps);
30733        this.last.setDisabled(ap == ps);
30734        this.loading.enable();
30735        this.updateInfo();
30736     },
30737
30738     // private
30739     getPageData : function(){
30740         var total = this.ds.getTotalCount();
30741         return {
30742             total : total,
30743             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30744             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30745         };
30746     },
30747
30748     // private
30749     onLoadError : function(){
30750         this.loading.enable();
30751     },
30752
30753     // private
30754     onPagingKeydown : function(e){
30755         var k = e.getKey();
30756         var d = this.getPageData();
30757         if(k == e.RETURN){
30758             var v = this.field.dom.value, pageNum;
30759             if(!v || isNaN(pageNum = parseInt(v, 10))){
30760                 this.field.dom.value = d.activePage;
30761                 return;
30762             }
30763             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30764             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30765             e.stopEvent();
30766         }
30767         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))
30768         {
30769           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30770           this.field.dom.value = pageNum;
30771           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30772           e.stopEvent();
30773         }
30774         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30775         {
30776           var v = this.field.dom.value, pageNum; 
30777           var increment = (e.shiftKey) ? 10 : 1;
30778           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30779             increment *= -1;
30780           }
30781           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30782             this.field.dom.value = d.activePage;
30783             return;
30784           }
30785           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30786           {
30787             this.field.dom.value = parseInt(v, 10) + increment;
30788             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30789             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30790           }
30791           e.stopEvent();
30792         }
30793     },
30794
30795     // private
30796     beforeLoad : function(){
30797         if(this.loading){
30798             this.loading.disable();
30799         }
30800     },
30801
30802     // private
30803     onClick : function(which){
30804         var ds = this.ds;
30805         switch(which){
30806             case "first":
30807                 ds.load({params:{start: 0, limit: this.pageSize}});
30808             break;
30809             case "prev":
30810                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30811             break;
30812             case "next":
30813                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30814             break;
30815             case "last":
30816                 var total = ds.getTotalCount();
30817                 var extra = total % this.pageSize;
30818                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30819                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30820             break;
30821             case "refresh":
30822                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30823             break;
30824         }
30825     },
30826
30827     /**
30828      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30829      * @param {Roo.data.Store} store The data store to unbind
30830      */
30831     unbind : function(ds){
30832         ds.un("beforeload", this.beforeLoad, this);
30833         ds.un("load", this.onLoad, this);
30834         ds.un("loadexception", this.onLoadError, this);
30835         ds.un("remove", this.updateInfo, this);
30836         ds.un("add", this.updateInfo, this);
30837         this.ds = undefined;
30838     },
30839
30840     /**
30841      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30842      * @param {Roo.data.Store} store The data store to bind
30843      */
30844     bind : function(ds){
30845         ds.on("beforeload", this.beforeLoad, this);
30846         ds.on("load", this.onLoad, this);
30847         ds.on("loadexception", this.onLoadError, this);
30848         ds.on("remove", this.updateInfo, this);
30849         ds.on("add", this.updateInfo, this);
30850         this.ds = ds;
30851     }
30852 });/*
30853  * Based on:
30854  * Ext JS Library 1.1.1
30855  * Copyright(c) 2006-2007, Ext JS, LLC.
30856  *
30857  * Originally Released Under LGPL - original licence link has changed is not relivant.
30858  *
30859  * Fork - LGPL
30860  * <script type="text/javascript">
30861  */
30862
30863 /**
30864  * @class Roo.Resizable
30865  * @extends Roo.util.Observable
30866  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30867  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30868  * 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
30869  * the element will be wrapped for you automatically.</p>
30870  * <p>Here is the list of valid resize handles:</p>
30871  * <pre>
30872 Value   Description
30873 ------  -------------------
30874  'n'     north
30875  's'     south
30876  'e'     east
30877  'w'     west
30878  'nw'    northwest
30879  'sw'    southwest
30880  'se'    southeast
30881  'ne'    northeast
30882  'hd'    horizontal drag
30883  'all'   all
30884 </pre>
30885  * <p>Here's an example showing the creation of a typical Resizable:</p>
30886  * <pre><code>
30887 var resizer = new Roo.Resizable("element-id", {
30888     handles: 'all',
30889     minWidth: 200,
30890     minHeight: 100,
30891     maxWidth: 500,
30892     maxHeight: 400,
30893     pinned: true
30894 });
30895 resizer.on("resize", myHandler);
30896 </code></pre>
30897  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30898  * resizer.east.setDisplayed(false);</p>
30899  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30900  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30901  * resize operation's new size (defaults to [0, 0])
30902  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30903  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30904  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30905  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30906  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30907  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30908  * @cfg {Number} width The width of the element in pixels (defaults to null)
30909  * @cfg {Number} height The height of the element in pixels (defaults to null)
30910  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30911  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30912  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30913  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30914  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30915  * in favor of the handles config option (defaults to false)
30916  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30917  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30918  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30919  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30920  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30921  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30922  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30923  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30924  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30925  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30926  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30927  * @constructor
30928  * Create a new resizable component
30929  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30930  * @param {Object} config configuration options
30931   */
30932 Roo.Resizable = function(el, config)
30933 {
30934     this.el = Roo.get(el);
30935
30936     if(config && config.wrap){
30937         config.resizeChild = this.el;
30938         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30939         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30940         this.el.setStyle("overflow", "hidden");
30941         this.el.setPositioning(config.resizeChild.getPositioning());
30942         config.resizeChild.clearPositioning();
30943         if(!config.width || !config.height){
30944             var csize = config.resizeChild.getSize();
30945             this.el.setSize(csize.width, csize.height);
30946         }
30947         if(config.pinned && !config.adjustments){
30948             config.adjustments = "auto";
30949         }
30950     }
30951
30952     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30953     this.proxy.unselectable();
30954     this.proxy.enableDisplayMode('block');
30955
30956     Roo.apply(this, config);
30957
30958     if(this.pinned){
30959         this.disableTrackOver = true;
30960         this.el.addClass("x-resizable-pinned");
30961     }
30962     // if the element isn't positioned, make it relative
30963     var position = this.el.getStyle("position");
30964     if(position != "absolute" && position != "fixed"){
30965         this.el.setStyle("position", "relative");
30966     }
30967     if(!this.handles){ // no handles passed, must be legacy style
30968         this.handles = 's,e,se';
30969         if(this.multiDirectional){
30970             this.handles += ',n,w';
30971         }
30972     }
30973     if(this.handles == "all"){
30974         this.handles = "n s e w ne nw se sw";
30975     }
30976     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30977     var ps = Roo.Resizable.positions;
30978     for(var i = 0, len = hs.length; i < len; i++){
30979         if(hs[i] && ps[hs[i]]){
30980             var pos = ps[hs[i]];
30981             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30982         }
30983     }
30984     // legacy
30985     this.corner = this.southeast;
30986     
30987     // updateBox = the box can move..
30988     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30989         this.updateBox = true;
30990     }
30991
30992     this.activeHandle = null;
30993
30994     if(this.resizeChild){
30995         if(typeof this.resizeChild == "boolean"){
30996             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30997         }else{
30998             this.resizeChild = Roo.get(this.resizeChild, true);
30999         }
31000     }
31001     
31002     if(this.adjustments == "auto"){
31003         var rc = this.resizeChild;
31004         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31005         if(rc && (hw || hn)){
31006             rc.position("relative");
31007             rc.setLeft(hw ? hw.el.getWidth() : 0);
31008             rc.setTop(hn ? hn.el.getHeight() : 0);
31009         }
31010         this.adjustments = [
31011             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31012             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31013         ];
31014     }
31015
31016     if(this.draggable){
31017         this.dd = this.dynamic ?
31018             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31019         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31020     }
31021
31022     // public events
31023     this.addEvents({
31024         /**
31025          * @event beforeresize
31026          * Fired before resize is allowed. Set enabled to false to cancel resize.
31027          * @param {Roo.Resizable} this
31028          * @param {Roo.EventObject} e The mousedown event
31029          */
31030         "beforeresize" : true,
31031         /**
31032          * @event resizing
31033          * Fired a resizing.
31034          * @param {Roo.Resizable} this
31035          * @param {Number} x The new x position
31036          * @param {Number} y The new y position
31037          * @param {Number} w The new w width
31038          * @param {Number} h The new h hight
31039          * @param {Roo.EventObject} e The mouseup event
31040          */
31041         "resizing" : true,
31042         /**
31043          * @event resize
31044          * Fired after a resize.
31045          * @param {Roo.Resizable} this
31046          * @param {Number} width The new width
31047          * @param {Number} height The new height
31048          * @param {Roo.EventObject} e The mouseup event
31049          */
31050         "resize" : true
31051     });
31052
31053     if(this.width !== null && this.height !== null){
31054         this.resizeTo(this.width, this.height);
31055     }else{
31056         this.updateChildSize();
31057     }
31058     if(Roo.isIE){
31059         this.el.dom.style.zoom = 1;
31060     }
31061     Roo.Resizable.superclass.constructor.call(this);
31062 };
31063
31064 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31065         resizeChild : false,
31066         adjustments : [0, 0],
31067         minWidth : 5,
31068         minHeight : 5,
31069         maxWidth : 10000,
31070         maxHeight : 10000,
31071         enabled : true,
31072         animate : false,
31073         duration : .35,
31074         dynamic : false,
31075         handles : false,
31076         multiDirectional : false,
31077         disableTrackOver : false,
31078         easing : 'easeOutStrong',
31079         widthIncrement : 0,
31080         heightIncrement : 0,
31081         pinned : false,
31082         width : null,
31083         height : null,
31084         preserveRatio : false,
31085         transparent: false,
31086         minX: 0,
31087         minY: 0,
31088         draggable: false,
31089
31090         /**
31091          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31092          */
31093         constrainTo: undefined,
31094         /**
31095          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31096          */
31097         resizeRegion: undefined,
31098
31099
31100     /**
31101      * Perform a manual resize
31102      * @param {Number} width
31103      * @param {Number} height
31104      */
31105     resizeTo : function(width, height){
31106         this.el.setSize(width, height);
31107         this.updateChildSize();
31108         this.fireEvent("resize", this, width, height, null);
31109     },
31110
31111     // private
31112     startSizing : function(e, handle){
31113         this.fireEvent("beforeresize", this, e);
31114         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31115
31116             if(!this.overlay){
31117                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31118                 this.overlay.unselectable();
31119                 this.overlay.enableDisplayMode("block");
31120                 this.overlay.on("mousemove", this.onMouseMove, this);
31121                 this.overlay.on("mouseup", this.onMouseUp, this);
31122             }
31123             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31124
31125             this.resizing = true;
31126             this.startBox = this.el.getBox();
31127             this.startPoint = e.getXY();
31128             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31129                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31130
31131             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31132             this.overlay.show();
31133
31134             if(this.constrainTo) {
31135                 var ct = Roo.get(this.constrainTo);
31136                 this.resizeRegion = ct.getRegion().adjust(
31137                     ct.getFrameWidth('t'),
31138                     ct.getFrameWidth('l'),
31139                     -ct.getFrameWidth('b'),
31140                     -ct.getFrameWidth('r')
31141                 );
31142             }
31143
31144             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31145             this.proxy.show();
31146             this.proxy.setBox(this.startBox);
31147             if(!this.dynamic){
31148                 this.proxy.setStyle('visibility', 'visible');
31149             }
31150         }
31151     },
31152
31153     // private
31154     onMouseDown : function(handle, e){
31155         if(this.enabled){
31156             e.stopEvent();
31157             this.activeHandle = handle;
31158             this.startSizing(e, handle);
31159         }
31160     },
31161
31162     // private
31163     onMouseUp : function(e){
31164         var size = this.resizeElement();
31165         this.resizing = false;
31166         this.handleOut();
31167         this.overlay.hide();
31168         this.proxy.hide();
31169         this.fireEvent("resize", this, size.width, size.height, e);
31170     },
31171
31172     // private
31173     updateChildSize : function(){
31174         
31175         if(this.resizeChild){
31176             var el = this.el;
31177             var child = this.resizeChild;
31178             var adj = this.adjustments;
31179             if(el.dom.offsetWidth){
31180                 var b = el.getSize(true);
31181                 child.setSize(b.width+adj[0], b.height+adj[1]);
31182             }
31183             // Second call here for IE
31184             // The first call enables instant resizing and
31185             // the second call corrects scroll bars if they
31186             // exist
31187             if(Roo.isIE){
31188                 setTimeout(function(){
31189                     if(el.dom.offsetWidth){
31190                         var b = el.getSize(true);
31191                         child.setSize(b.width+adj[0], b.height+adj[1]);
31192                     }
31193                 }, 10);
31194             }
31195         }
31196     },
31197
31198     // private
31199     snap : function(value, inc, min){
31200         if(!inc || !value) {
31201             return value;
31202         }
31203         var newValue = value;
31204         var m = value % inc;
31205         if(m > 0){
31206             if(m > (inc/2)){
31207                 newValue = value + (inc-m);
31208             }else{
31209                 newValue = value - m;
31210             }
31211         }
31212         return Math.max(min, newValue);
31213     },
31214
31215     // private
31216     resizeElement : function(){
31217         var box = this.proxy.getBox();
31218         if(this.updateBox){
31219             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31220         }else{
31221             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31222         }
31223         this.updateChildSize();
31224         if(!this.dynamic){
31225             this.proxy.hide();
31226         }
31227         return box;
31228     },
31229
31230     // private
31231     constrain : function(v, diff, m, mx){
31232         if(v - diff < m){
31233             diff = v - m;
31234         }else if(v - diff > mx){
31235             diff = mx - v;
31236         }
31237         return diff;
31238     },
31239
31240     // private
31241     onMouseMove : function(e){
31242         
31243         if(this.enabled){
31244             try{// try catch so if something goes wrong the user doesn't get hung
31245
31246             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31247                 return;
31248             }
31249
31250             //var curXY = this.startPoint;
31251             var curSize = this.curSize || this.startBox;
31252             var x = this.startBox.x, y = this.startBox.y;
31253             var ox = x, oy = y;
31254             var w = curSize.width, h = curSize.height;
31255             var ow = w, oh = h;
31256             var mw = this.minWidth, mh = this.minHeight;
31257             var mxw = this.maxWidth, mxh = this.maxHeight;
31258             var wi = this.widthIncrement;
31259             var hi = this.heightIncrement;
31260
31261             var eventXY = e.getXY();
31262             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31263             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31264
31265             var pos = this.activeHandle.position;
31266
31267             switch(pos){
31268                 case "east":
31269                     w += diffX;
31270                     w = Math.min(Math.max(mw, w), mxw);
31271                     break;
31272              
31273                 case "south":
31274                     h += diffY;
31275                     h = Math.min(Math.max(mh, h), mxh);
31276                     break;
31277                 case "southeast":
31278                     w += diffX;
31279                     h += diffY;
31280                     w = Math.min(Math.max(mw, w), mxw);
31281                     h = Math.min(Math.max(mh, h), mxh);
31282                     break;
31283                 case "north":
31284                     diffY = this.constrain(h, diffY, mh, mxh);
31285                     y += diffY;
31286                     h -= diffY;
31287                     break;
31288                 case "hdrag":
31289                     
31290                     if (wi) {
31291                         var adiffX = Math.abs(diffX);
31292                         var sub = (adiffX % wi); // how much 
31293                         if (sub > (wi/2)) { // far enough to snap
31294                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31295                         } else {
31296                             // remove difference.. 
31297                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31298                         }
31299                     }
31300                     x += diffX;
31301                     x = Math.max(this.minX, x);
31302                     break;
31303                 case "west":
31304                     diffX = this.constrain(w, diffX, mw, mxw);
31305                     x += diffX;
31306                     w -= diffX;
31307                     break;
31308                 case "northeast":
31309                     w += diffX;
31310                     w = Math.min(Math.max(mw, w), mxw);
31311                     diffY = this.constrain(h, diffY, mh, mxh);
31312                     y += diffY;
31313                     h -= diffY;
31314                     break;
31315                 case "northwest":
31316                     diffX = this.constrain(w, diffX, mw, mxw);
31317                     diffY = this.constrain(h, diffY, mh, mxh);
31318                     y += diffY;
31319                     h -= diffY;
31320                     x += diffX;
31321                     w -= diffX;
31322                     break;
31323                case "southwest":
31324                     diffX = this.constrain(w, diffX, mw, mxw);
31325                     h += diffY;
31326                     h = Math.min(Math.max(mh, h), mxh);
31327                     x += diffX;
31328                     w -= diffX;
31329                     break;
31330             }
31331
31332             var sw = this.snap(w, wi, mw);
31333             var sh = this.snap(h, hi, mh);
31334             if(sw != w || sh != h){
31335                 switch(pos){
31336                     case "northeast":
31337                         y -= sh - h;
31338                     break;
31339                     case "north":
31340                         y -= sh - h;
31341                         break;
31342                     case "southwest":
31343                         x -= sw - w;
31344                     break;
31345                     case "west":
31346                         x -= sw - w;
31347                         break;
31348                     case "northwest":
31349                         x -= sw - w;
31350                         y -= sh - h;
31351                     break;
31352                 }
31353                 w = sw;
31354                 h = sh;
31355             }
31356
31357             if(this.preserveRatio){
31358                 switch(pos){
31359                     case "southeast":
31360                     case "east":
31361                         h = oh * (w/ow);
31362                         h = Math.min(Math.max(mh, h), mxh);
31363                         w = ow * (h/oh);
31364                        break;
31365                     case "south":
31366                         w = ow * (h/oh);
31367                         w = Math.min(Math.max(mw, w), mxw);
31368                         h = oh * (w/ow);
31369                         break;
31370                     case "northeast":
31371                         w = ow * (h/oh);
31372                         w = Math.min(Math.max(mw, w), mxw);
31373                         h = oh * (w/ow);
31374                     break;
31375                     case "north":
31376                         var tw = w;
31377                         w = ow * (h/oh);
31378                         w = Math.min(Math.max(mw, w), mxw);
31379                         h = oh * (w/ow);
31380                         x += (tw - w) / 2;
31381                         break;
31382                     case "southwest":
31383                         h = oh * (w/ow);
31384                         h = Math.min(Math.max(mh, h), mxh);
31385                         var tw = w;
31386                         w = ow * (h/oh);
31387                         x += tw - w;
31388                         break;
31389                     case "west":
31390                         var th = h;
31391                         h = oh * (w/ow);
31392                         h = Math.min(Math.max(mh, h), mxh);
31393                         y += (th - h) / 2;
31394                         var tw = w;
31395                         w = ow * (h/oh);
31396                         x += tw - w;
31397                        break;
31398                     case "northwest":
31399                         var tw = w;
31400                         var th = h;
31401                         h = oh * (w/ow);
31402                         h = Math.min(Math.max(mh, h), mxh);
31403                         w = ow * (h/oh);
31404                         y += th - h;
31405                         x += tw - w;
31406                        break;
31407
31408                 }
31409             }
31410             if (pos == 'hdrag') {
31411                 w = ow;
31412             }
31413             this.proxy.setBounds(x, y, w, h);
31414             if(this.dynamic){
31415                 this.resizeElement();
31416             }
31417             }catch(e){}
31418         }
31419         this.fireEvent("resizing", this, x, y, w, h, e);
31420     },
31421
31422     // private
31423     handleOver : function(){
31424         if(this.enabled){
31425             this.el.addClass("x-resizable-over");
31426         }
31427     },
31428
31429     // private
31430     handleOut : function(){
31431         if(!this.resizing){
31432             this.el.removeClass("x-resizable-over");
31433         }
31434     },
31435
31436     /**
31437      * Returns the element this component is bound to.
31438      * @return {Roo.Element}
31439      */
31440     getEl : function(){
31441         return this.el;
31442     },
31443
31444     /**
31445      * Returns the resizeChild element (or null).
31446      * @return {Roo.Element}
31447      */
31448     getResizeChild : function(){
31449         return this.resizeChild;
31450     },
31451     groupHandler : function()
31452     {
31453         
31454     },
31455     /**
31456      * Destroys this resizable. If the element was wrapped and
31457      * removeEl is not true then the element remains.
31458      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31459      */
31460     destroy : function(removeEl){
31461         this.proxy.remove();
31462         if(this.overlay){
31463             this.overlay.removeAllListeners();
31464             this.overlay.remove();
31465         }
31466         var ps = Roo.Resizable.positions;
31467         for(var k in ps){
31468             if(typeof ps[k] != "function" && this[ps[k]]){
31469                 var h = this[ps[k]];
31470                 h.el.removeAllListeners();
31471                 h.el.remove();
31472             }
31473         }
31474         if(removeEl){
31475             this.el.update("");
31476             this.el.remove();
31477         }
31478     }
31479 });
31480
31481 // private
31482 // hash to map config positions to true positions
31483 Roo.Resizable.positions = {
31484     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31485     hd: "hdrag"
31486 };
31487
31488 // private
31489 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31490     if(!this.tpl){
31491         // only initialize the template if resizable is used
31492         var tpl = Roo.DomHelper.createTemplate(
31493             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31494         );
31495         tpl.compile();
31496         Roo.Resizable.Handle.prototype.tpl = tpl;
31497     }
31498     this.position = pos;
31499     this.rz = rz;
31500     // show north drag fro topdra
31501     var handlepos = pos == 'hdrag' ? 'north' : pos;
31502     
31503     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31504     if (pos == 'hdrag') {
31505         this.el.setStyle('cursor', 'pointer');
31506     }
31507     this.el.unselectable();
31508     if(transparent){
31509         this.el.setOpacity(0);
31510     }
31511     this.el.on("mousedown", this.onMouseDown, this);
31512     if(!disableTrackOver){
31513         this.el.on("mouseover", this.onMouseOver, this);
31514         this.el.on("mouseout", this.onMouseOut, this);
31515     }
31516 };
31517
31518 // private
31519 Roo.Resizable.Handle.prototype = {
31520     afterResize : function(rz){
31521         Roo.log('after?');
31522         // do nothing
31523     },
31524     // private
31525     onMouseDown : function(e){
31526         this.rz.onMouseDown(this, e);
31527     },
31528     // private
31529     onMouseOver : function(e){
31530         this.rz.handleOver(this, e);
31531     },
31532     // private
31533     onMouseOut : function(e){
31534         this.rz.handleOut(this, e);
31535     }
31536 };/*
31537  * Based on:
31538  * Ext JS Library 1.1.1
31539  * Copyright(c) 2006-2007, Ext JS, LLC.
31540  *
31541  * Originally Released Under LGPL - original licence link has changed is not relivant.
31542  *
31543  * Fork - LGPL
31544  * <script type="text/javascript">
31545  */
31546
31547 /**
31548  * @class Roo.Editor
31549  * @extends Roo.Component
31550  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31551  * @constructor
31552  * Create a new Editor
31553  * @param {Roo.form.Field} field The Field object (or descendant)
31554  * @param {Object} config The config object
31555  */
31556 Roo.Editor = function(field, config){
31557     Roo.Editor.superclass.constructor.call(this, config);
31558     this.field = field;
31559     this.addEvents({
31560         /**
31561              * @event beforestartedit
31562              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31563              * false from the handler of this event.
31564              * @param {Editor} this
31565              * @param {Roo.Element} boundEl The underlying element bound to this editor
31566              * @param {Mixed} value The field value being set
31567              */
31568         "beforestartedit" : true,
31569         /**
31570              * @event startedit
31571              * Fires when this editor is displayed
31572              * @param {Roo.Element} boundEl The underlying element bound to this editor
31573              * @param {Mixed} value The starting field value
31574              */
31575         "startedit" : true,
31576         /**
31577              * @event beforecomplete
31578              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31579              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31580              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31581              * event will not fire since no edit actually occurred.
31582              * @param {Editor} this
31583              * @param {Mixed} value The current field value
31584              * @param {Mixed} startValue The original field value
31585              */
31586         "beforecomplete" : true,
31587         /**
31588              * @event complete
31589              * Fires after editing is complete and any changed value has been written to the underlying field.
31590              * @param {Editor} this
31591              * @param {Mixed} value The current field value
31592              * @param {Mixed} startValue The original field value
31593              */
31594         "complete" : true,
31595         /**
31596          * @event specialkey
31597          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31598          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31599          * @param {Roo.form.Field} this
31600          * @param {Roo.EventObject} e The event object
31601          */
31602         "specialkey" : true
31603     });
31604 };
31605
31606 Roo.extend(Roo.Editor, Roo.Component, {
31607     /**
31608      * @cfg {Boolean/String} autosize
31609      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31610      * or "height" to adopt the height only (defaults to false)
31611      */
31612     /**
31613      * @cfg {Boolean} revertInvalid
31614      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31615      * validation fails (defaults to true)
31616      */
31617     /**
31618      * @cfg {Boolean} ignoreNoChange
31619      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31620      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31621      * will never be ignored.
31622      */
31623     /**
31624      * @cfg {Boolean} hideEl
31625      * False to keep the bound element visible while the editor is displayed (defaults to true)
31626      */
31627     /**
31628      * @cfg {Mixed} value
31629      * The data value of the underlying field (defaults to "")
31630      */
31631     value : "",
31632     /**
31633      * @cfg {String} alignment
31634      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31635      */
31636     alignment: "c-c?",
31637     /**
31638      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31639      * for bottom-right shadow (defaults to "frame")
31640      */
31641     shadow : "frame",
31642     /**
31643      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31644      */
31645     constrain : false,
31646     /**
31647      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31648      */
31649     completeOnEnter : false,
31650     /**
31651      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31652      */
31653     cancelOnEsc : false,
31654     /**
31655      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31656      */
31657     updateEl : false,
31658
31659     // private
31660     onRender : function(ct, position){
31661         this.el = new Roo.Layer({
31662             shadow: this.shadow,
31663             cls: "x-editor",
31664             parentEl : ct,
31665             shim : this.shim,
31666             shadowOffset:4,
31667             id: this.id,
31668             constrain: this.constrain
31669         });
31670         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31671         if(this.field.msgTarget != 'title'){
31672             this.field.msgTarget = 'qtip';
31673         }
31674         this.field.render(this.el);
31675         if(Roo.isGecko){
31676             this.field.el.dom.setAttribute('autocomplete', 'off');
31677         }
31678         this.field.on("specialkey", this.onSpecialKey, this);
31679         if(this.swallowKeys){
31680             this.field.el.swallowEvent(['keydown','keypress']);
31681         }
31682         this.field.show();
31683         this.field.on("blur", this.onBlur, this);
31684         if(this.field.grow){
31685             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31686         }
31687     },
31688
31689     onSpecialKey : function(field, e)
31690     {
31691         //Roo.log('editor onSpecialKey');
31692         if(this.completeOnEnter && e.getKey() == e.ENTER){
31693             e.stopEvent();
31694             this.completeEdit();
31695             return;
31696         }
31697         // do not fire special key otherwise it might hide close the editor...
31698         if(e.getKey() == e.ENTER){    
31699             return;
31700         }
31701         if(this.cancelOnEsc && e.getKey() == e.ESC){
31702             this.cancelEdit();
31703             return;
31704         } 
31705         this.fireEvent('specialkey', field, e);
31706     
31707     },
31708
31709     /**
31710      * Starts the editing process and shows the editor.
31711      * @param {String/HTMLElement/Element} el The element to edit
31712      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31713       * to the innerHTML of el.
31714      */
31715     startEdit : function(el, value){
31716         if(this.editing){
31717             this.completeEdit();
31718         }
31719         this.boundEl = Roo.get(el);
31720         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31721         if(!this.rendered){
31722             this.render(this.parentEl || document.body);
31723         }
31724         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31725             return;
31726         }
31727         this.startValue = v;
31728         this.field.setValue(v);
31729         if(this.autoSize){
31730             var sz = this.boundEl.getSize();
31731             switch(this.autoSize){
31732                 case "width":
31733                 this.setSize(sz.width,  "");
31734                 break;
31735                 case "height":
31736                 this.setSize("",  sz.height);
31737                 break;
31738                 default:
31739                 this.setSize(sz.width,  sz.height);
31740             }
31741         }
31742         this.el.alignTo(this.boundEl, this.alignment);
31743         this.editing = true;
31744         if(Roo.QuickTips){
31745             Roo.QuickTips.disable();
31746         }
31747         this.show();
31748     },
31749
31750     /**
31751      * Sets the height and width of this editor.
31752      * @param {Number} width The new width
31753      * @param {Number} height The new height
31754      */
31755     setSize : function(w, h){
31756         this.field.setSize(w, h);
31757         if(this.el){
31758             this.el.sync();
31759         }
31760     },
31761
31762     /**
31763      * Realigns the editor to the bound field based on the current alignment config value.
31764      */
31765     realign : function(){
31766         this.el.alignTo(this.boundEl, this.alignment);
31767     },
31768
31769     /**
31770      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31771      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31772      */
31773     completeEdit : function(remainVisible){
31774         if(!this.editing){
31775             return;
31776         }
31777         var v = this.getValue();
31778         if(this.revertInvalid !== false && !this.field.isValid()){
31779             v = this.startValue;
31780             this.cancelEdit(true);
31781         }
31782         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31783             this.editing = false;
31784             this.hide();
31785             return;
31786         }
31787         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31788             this.editing = false;
31789             if(this.updateEl && this.boundEl){
31790                 this.boundEl.update(v);
31791             }
31792             if(remainVisible !== true){
31793                 this.hide();
31794             }
31795             this.fireEvent("complete", this, v, this.startValue);
31796         }
31797     },
31798
31799     // private
31800     onShow : function(){
31801         this.el.show();
31802         if(this.hideEl !== false){
31803             this.boundEl.hide();
31804         }
31805         this.field.show();
31806         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31807             this.fixIEFocus = true;
31808             this.deferredFocus.defer(50, this);
31809         }else{
31810             this.field.focus();
31811         }
31812         this.fireEvent("startedit", this.boundEl, this.startValue);
31813     },
31814
31815     deferredFocus : function(){
31816         if(this.editing){
31817             this.field.focus();
31818         }
31819     },
31820
31821     /**
31822      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31823      * reverted to the original starting value.
31824      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31825      * cancel (defaults to false)
31826      */
31827     cancelEdit : function(remainVisible){
31828         if(this.editing){
31829             this.setValue(this.startValue);
31830             if(remainVisible !== true){
31831                 this.hide();
31832             }
31833         }
31834     },
31835
31836     // private
31837     onBlur : function(){
31838         if(this.allowBlur !== true && this.editing){
31839             this.completeEdit();
31840         }
31841     },
31842
31843     // private
31844     onHide : function(){
31845         if(this.editing){
31846             this.completeEdit();
31847             return;
31848         }
31849         this.field.blur();
31850         if(this.field.collapse){
31851             this.field.collapse();
31852         }
31853         this.el.hide();
31854         if(this.hideEl !== false){
31855             this.boundEl.show();
31856         }
31857         if(Roo.QuickTips){
31858             Roo.QuickTips.enable();
31859         }
31860     },
31861
31862     /**
31863      * Sets the data value of the editor
31864      * @param {Mixed} value Any valid value supported by the underlying field
31865      */
31866     setValue : function(v){
31867         this.field.setValue(v);
31868     },
31869
31870     /**
31871      * Gets the data value of the editor
31872      * @return {Mixed} The data value
31873      */
31874     getValue : function(){
31875         return this.field.getValue();
31876     }
31877 });/*
31878  * Based on:
31879  * Ext JS Library 1.1.1
31880  * Copyright(c) 2006-2007, Ext JS, LLC.
31881  *
31882  * Originally Released Under LGPL - original licence link has changed is not relivant.
31883  *
31884  * Fork - LGPL
31885  * <script type="text/javascript">
31886  */
31887  
31888 /**
31889  * @class Roo.BasicDialog
31890  * @extends Roo.util.Observable
31891  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31892  * <pre><code>
31893 var dlg = new Roo.BasicDialog("my-dlg", {
31894     height: 200,
31895     width: 300,
31896     minHeight: 100,
31897     minWidth: 150,
31898     modal: true,
31899     proxyDrag: true,
31900     shadow: true
31901 });
31902 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31903 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31904 dlg.addButton('Cancel', dlg.hide, dlg);
31905 dlg.show();
31906 </code></pre>
31907   <b>A Dialog should always be a direct child of the body element.</b>
31908  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31909  * @cfg {String} title Default text to display in the title bar (defaults to null)
31910  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31911  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31912  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31913  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31914  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31915  * (defaults to null with no animation)
31916  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31917  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31918  * property for valid values (defaults to 'all')
31919  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31920  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31921  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31922  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31923  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31924  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31925  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31926  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31927  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31928  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31929  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31930  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31931  * draggable = true (defaults to false)
31932  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31933  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31934  * shadow (defaults to false)
31935  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31936  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31937  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31938  * @cfg {Array} buttons Array of buttons
31939  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31940  * @constructor
31941  * Create a new BasicDialog.
31942  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31943  * @param {Object} config Configuration options
31944  */
31945 Roo.BasicDialog = function(el, config){
31946     this.el = Roo.get(el);
31947     var dh = Roo.DomHelper;
31948     if(!this.el && config && config.autoCreate){
31949         if(typeof config.autoCreate == "object"){
31950             if(!config.autoCreate.id){
31951                 config.autoCreate.id = el;
31952             }
31953             this.el = dh.append(document.body,
31954                         config.autoCreate, true);
31955         }else{
31956             this.el = dh.append(document.body,
31957                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31958         }
31959     }
31960     el = this.el;
31961     el.setDisplayed(true);
31962     el.hide = this.hideAction;
31963     this.id = el.id;
31964     el.addClass("x-dlg");
31965
31966     Roo.apply(this, config);
31967
31968     this.proxy = el.createProxy("x-dlg-proxy");
31969     this.proxy.hide = this.hideAction;
31970     this.proxy.setOpacity(.5);
31971     this.proxy.hide();
31972
31973     if(config.width){
31974         el.setWidth(config.width);
31975     }
31976     if(config.height){
31977         el.setHeight(config.height);
31978     }
31979     this.size = el.getSize();
31980     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31981         this.xy = [config.x,config.y];
31982     }else{
31983         this.xy = el.getCenterXY(true);
31984     }
31985     /** The header element @type Roo.Element */
31986     this.header = el.child("> .x-dlg-hd");
31987     /** The body element @type Roo.Element */
31988     this.body = el.child("> .x-dlg-bd");
31989     /** The footer element @type Roo.Element */
31990     this.footer = el.child("> .x-dlg-ft");
31991
31992     if(!this.header){
31993         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31994     }
31995     if(!this.body){
31996         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31997     }
31998
31999     this.header.unselectable();
32000     if(this.title){
32001         this.header.update(this.title);
32002     }
32003     // this element allows the dialog to be focused for keyboard event
32004     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32005     this.focusEl.swallowEvent("click", true);
32006
32007     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32008
32009     // wrap the body and footer for special rendering
32010     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32011     if(this.footer){
32012         this.bwrap.dom.appendChild(this.footer.dom);
32013     }
32014
32015     this.bg = this.el.createChild({
32016         tag: "div", cls:"x-dlg-bg",
32017         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32018     });
32019     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32020
32021
32022     if(this.autoScroll !== false && !this.autoTabs){
32023         this.body.setStyle("overflow", "auto");
32024     }
32025
32026     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32027
32028     if(this.closable !== false){
32029         this.el.addClass("x-dlg-closable");
32030         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32031         this.close.on("click", this.closeClick, this);
32032         this.close.addClassOnOver("x-dlg-close-over");
32033     }
32034     if(this.collapsible !== false){
32035         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32036         this.collapseBtn.on("click", this.collapseClick, this);
32037         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32038         this.header.on("dblclick", this.collapseClick, this);
32039     }
32040     if(this.resizable !== false){
32041         this.el.addClass("x-dlg-resizable");
32042         this.resizer = new Roo.Resizable(el, {
32043             minWidth: this.minWidth || 80,
32044             minHeight:this.minHeight || 80,
32045             handles: this.resizeHandles || "all",
32046             pinned: true
32047         });
32048         this.resizer.on("beforeresize", this.beforeResize, this);
32049         this.resizer.on("resize", this.onResize, this);
32050     }
32051     if(this.draggable !== false){
32052         el.addClass("x-dlg-draggable");
32053         if (!this.proxyDrag) {
32054             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32055         }
32056         else {
32057             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32058         }
32059         dd.setHandleElId(this.header.id);
32060         dd.endDrag = this.endMove.createDelegate(this);
32061         dd.startDrag = this.startMove.createDelegate(this);
32062         dd.onDrag = this.onDrag.createDelegate(this);
32063         dd.scroll = false;
32064         this.dd = dd;
32065     }
32066     if(this.modal){
32067         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32068         this.mask.enableDisplayMode("block");
32069         this.mask.hide();
32070         this.el.addClass("x-dlg-modal");
32071     }
32072     if(this.shadow){
32073         this.shadow = new Roo.Shadow({
32074             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32075             offset : this.shadowOffset
32076         });
32077     }else{
32078         this.shadowOffset = 0;
32079     }
32080     if(Roo.useShims && this.shim !== false){
32081         this.shim = this.el.createShim();
32082         this.shim.hide = this.hideAction;
32083         this.shim.hide();
32084     }else{
32085         this.shim = false;
32086     }
32087     if(this.autoTabs){
32088         this.initTabs();
32089     }
32090     if (this.buttons) { 
32091         var bts= this.buttons;
32092         this.buttons = [];
32093         Roo.each(bts, function(b) {
32094             this.addButton(b);
32095         }, this);
32096     }
32097     
32098     
32099     this.addEvents({
32100         /**
32101          * @event keydown
32102          * Fires when a key is pressed
32103          * @param {Roo.BasicDialog} this
32104          * @param {Roo.EventObject} e
32105          */
32106         "keydown" : true,
32107         /**
32108          * @event move
32109          * Fires when this dialog is moved by the user.
32110          * @param {Roo.BasicDialog} this
32111          * @param {Number} x The new page X
32112          * @param {Number} y The new page Y
32113          */
32114         "move" : true,
32115         /**
32116          * @event resize
32117          * Fires when this dialog is resized by the user.
32118          * @param {Roo.BasicDialog} this
32119          * @param {Number} width The new width
32120          * @param {Number} height The new height
32121          */
32122         "resize" : true,
32123         /**
32124          * @event beforehide
32125          * Fires before this dialog is hidden.
32126          * @param {Roo.BasicDialog} this
32127          */
32128         "beforehide" : true,
32129         /**
32130          * @event hide
32131          * Fires when this dialog is hidden.
32132          * @param {Roo.BasicDialog} this
32133          */
32134         "hide" : true,
32135         /**
32136          * @event beforeshow
32137          * Fires before this dialog is shown.
32138          * @param {Roo.BasicDialog} this
32139          */
32140         "beforeshow" : true,
32141         /**
32142          * @event show
32143          * Fires when this dialog is shown.
32144          * @param {Roo.BasicDialog} this
32145          */
32146         "show" : true
32147     });
32148     el.on("keydown", this.onKeyDown, this);
32149     el.on("mousedown", this.toFront, this);
32150     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32151     this.el.hide();
32152     Roo.DialogManager.register(this);
32153     Roo.BasicDialog.superclass.constructor.call(this);
32154 };
32155
32156 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32157     shadowOffset: Roo.isIE ? 6 : 5,
32158     minHeight: 80,
32159     minWidth: 200,
32160     minButtonWidth: 75,
32161     defaultButton: null,
32162     buttonAlign: "right",
32163     tabTag: 'div',
32164     firstShow: true,
32165
32166     /**
32167      * Sets the dialog title text
32168      * @param {String} text The title text to display
32169      * @return {Roo.BasicDialog} this
32170      */
32171     setTitle : function(text){
32172         this.header.update(text);
32173         return this;
32174     },
32175
32176     // private
32177     closeClick : function(){
32178         this.hide();
32179     },
32180
32181     // private
32182     collapseClick : function(){
32183         this[this.collapsed ? "expand" : "collapse"]();
32184     },
32185
32186     /**
32187      * Collapses the dialog to its minimized state (only the title bar is visible).
32188      * Equivalent to the user clicking the collapse dialog button.
32189      */
32190     collapse : function(){
32191         if(!this.collapsed){
32192             this.collapsed = true;
32193             this.el.addClass("x-dlg-collapsed");
32194             this.restoreHeight = this.el.getHeight();
32195             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32196         }
32197     },
32198
32199     /**
32200      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32201      * clicking the expand dialog button.
32202      */
32203     expand : function(){
32204         if(this.collapsed){
32205             this.collapsed = false;
32206             this.el.removeClass("x-dlg-collapsed");
32207             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32208         }
32209     },
32210
32211     /**
32212      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32213      * @return {Roo.TabPanel} The tabs component
32214      */
32215     initTabs : function(){
32216         var tabs = this.getTabs();
32217         while(tabs.getTab(0)){
32218             tabs.removeTab(0);
32219         }
32220         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32221             var dom = el.dom;
32222             tabs.addTab(Roo.id(dom), dom.title);
32223             dom.title = "";
32224         });
32225         tabs.activate(0);
32226         return tabs;
32227     },
32228
32229     // private
32230     beforeResize : function(){
32231         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32232     },
32233
32234     // private
32235     onResize : function(){
32236         this.refreshSize();
32237         this.syncBodyHeight();
32238         this.adjustAssets();
32239         this.focus();
32240         this.fireEvent("resize", this, this.size.width, this.size.height);
32241     },
32242
32243     // private
32244     onKeyDown : function(e){
32245         if(this.isVisible()){
32246             this.fireEvent("keydown", this, e);
32247         }
32248     },
32249
32250     /**
32251      * Resizes the dialog.
32252      * @param {Number} width
32253      * @param {Number} height
32254      * @return {Roo.BasicDialog} this
32255      */
32256     resizeTo : function(width, height){
32257         this.el.setSize(width, height);
32258         this.size = {width: width, height: height};
32259         this.syncBodyHeight();
32260         if(this.fixedcenter){
32261             this.center();
32262         }
32263         if(this.isVisible()){
32264             this.constrainXY();
32265             this.adjustAssets();
32266         }
32267         this.fireEvent("resize", this, width, height);
32268         return this;
32269     },
32270
32271
32272     /**
32273      * Resizes the dialog to fit the specified content size.
32274      * @param {Number} width
32275      * @param {Number} height
32276      * @return {Roo.BasicDialog} this
32277      */
32278     setContentSize : function(w, h){
32279         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32280         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32281         //if(!this.el.isBorderBox()){
32282             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32283             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32284         //}
32285         if(this.tabs){
32286             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32287             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32288         }
32289         this.resizeTo(w, h);
32290         return this;
32291     },
32292
32293     /**
32294      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32295      * executed in response to a particular key being pressed while the dialog is active.
32296      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32297      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32298      * @param {Function} fn The function to call
32299      * @param {Object} scope (optional) The scope of the function
32300      * @return {Roo.BasicDialog} this
32301      */
32302     addKeyListener : function(key, fn, scope){
32303         var keyCode, shift, ctrl, alt;
32304         if(typeof key == "object" && !(key instanceof Array)){
32305             keyCode = key["key"];
32306             shift = key["shift"];
32307             ctrl = key["ctrl"];
32308             alt = key["alt"];
32309         }else{
32310             keyCode = key;
32311         }
32312         var handler = function(dlg, e){
32313             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32314                 var k = e.getKey();
32315                 if(keyCode instanceof Array){
32316                     for(var i = 0, len = keyCode.length; i < len; i++){
32317                         if(keyCode[i] == k){
32318                           fn.call(scope || window, dlg, k, e);
32319                           return;
32320                         }
32321                     }
32322                 }else{
32323                     if(k == keyCode){
32324                         fn.call(scope || window, dlg, k, e);
32325                     }
32326                 }
32327             }
32328         };
32329         this.on("keydown", handler);
32330         return this;
32331     },
32332
32333     /**
32334      * Returns the TabPanel component (creates it if it doesn't exist).
32335      * Note: If you wish to simply check for the existence of tabs without creating them,
32336      * check for a null 'tabs' property.
32337      * @return {Roo.TabPanel} The tabs component
32338      */
32339     getTabs : function(){
32340         if(!this.tabs){
32341             this.el.addClass("x-dlg-auto-tabs");
32342             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32343             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32344         }
32345         return this.tabs;
32346     },
32347
32348     /**
32349      * Adds a button to the footer section of the dialog.
32350      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32351      * object or a valid Roo.DomHelper element config
32352      * @param {Function} handler The function called when the button is clicked
32353      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32354      * @return {Roo.Button} The new button
32355      */
32356     addButton : function(config, handler, scope){
32357         var dh = Roo.DomHelper;
32358         if(!this.footer){
32359             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32360         }
32361         if(!this.btnContainer){
32362             var tb = this.footer.createChild({
32363
32364                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32365                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32366             }, null, true);
32367             this.btnContainer = tb.firstChild.firstChild.firstChild;
32368         }
32369         var bconfig = {
32370             handler: handler,
32371             scope: scope,
32372             minWidth: this.minButtonWidth,
32373             hideParent:true
32374         };
32375         if(typeof config == "string"){
32376             bconfig.text = config;
32377         }else{
32378             if(config.tag){
32379                 bconfig.dhconfig = config;
32380             }else{
32381                 Roo.apply(bconfig, config);
32382             }
32383         }
32384         var fc = false;
32385         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32386             bconfig.position = Math.max(0, bconfig.position);
32387             fc = this.btnContainer.childNodes[bconfig.position];
32388         }
32389          
32390         var btn = new Roo.Button(
32391             fc ? 
32392                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32393                 : this.btnContainer.appendChild(document.createElement("td")),
32394             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32395             bconfig
32396         );
32397         this.syncBodyHeight();
32398         if(!this.buttons){
32399             /**
32400              * Array of all the buttons that have been added to this dialog via addButton
32401              * @type Array
32402              */
32403             this.buttons = [];
32404         }
32405         this.buttons.push(btn);
32406         return btn;
32407     },
32408
32409     /**
32410      * Sets the default button to be focused when the dialog is displayed.
32411      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32412      * @return {Roo.BasicDialog} this
32413      */
32414     setDefaultButton : function(btn){
32415         this.defaultButton = btn;
32416         return this;
32417     },
32418
32419     // private
32420     getHeaderFooterHeight : function(safe){
32421         var height = 0;
32422         if(this.header){
32423            height += this.header.getHeight();
32424         }
32425         if(this.footer){
32426            var fm = this.footer.getMargins();
32427             height += (this.footer.getHeight()+fm.top+fm.bottom);
32428         }
32429         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32430         height += this.centerBg.getPadding("tb");
32431         return height;
32432     },
32433
32434     // private
32435     syncBodyHeight : function()
32436     {
32437         var bd = this.body, // the text
32438             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32439             bw = this.bwrap;
32440         var height = this.size.height - this.getHeaderFooterHeight(false);
32441         bd.setHeight(height-bd.getMargins("tb"));
32442         var hh = this.header.getHeight();
32443         var h = this.size.height-hh;
32444         cb.setHeight(h);
32445         
32446         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32447         bw.setHeight(h-cb.getPadding("tb"));
32448         
32449         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32450         bd.setWidth(bw.getWidth(true));
32451         if(this.tabs){
32452             this.tabs.syncHeight();
32453             if(Roo.isIE){
32454                 this.tabs.el.repaint();
32455             }
32456         }
32457     },
32458
32459     /**
32460      * Restores the previous state of the dialog if Roo.state is configured.
32461      * @return {Roo.BasicDialog} this
32462      */
32463     restoreState : function(){
32464         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32465         if(box && box.width){
32466             this.xy = [box.x, box.y];
32467             this.resizeTo(box.width, box.height);
32468         }
32469         return this;
32470     },
32471
32472     // private
32473     beforeShow : function(){
32474         this.expand();
32475         if(this.fixedcenter){
32476             this.xy = this.el.getCenterXY(true);
32477         }
32478         if(this.modal){
32479             Roo.get(document.body).addClass("x-body-masked");
32480             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32481             this.mask.show();
32482         }
32483         this.constrainXY();
32484     },
32485
32486     // private
32487     animShow : function(){
32488         var b = Roo.get(this.animateTarget).getBox();
32489         this.proxy.setSize(b.width, b.height);
32490         this.proxy.setLocation(b.x, b.y);
32491         this.proxy.show();
32492         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32493                     true, .35, this.showEl.createDelegate(this));
32494     },
32495
32496     /**
32497      * Shows the dialog.
32498      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32499      * @return {Roo.BasicDialog} this
32500      */
32501     show : function(animateTarget){
32502         if (this.fireEvent("beforeshow", this) === false){
32503             return;
32504         }
32505         if(this.syncHeightBeforeShow){
32506             this.syncBodyHeight();
32507         }else if(this.firstShow){
32508             this.firstShow = false;
32509             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32510         }
32511         this.animateTarget = animateTarget || this.animateTarget;
32512         if(!this.el.isVisible()){
32513             this.beforeShow();
32514             if(this.animateTarget && Roo.get(this.animateTarget)){
32515                 this.animShow();
32516             }else{
32517                 this.showEl();
32518             }
32519         }
32520         return this;
32521     },
32522
32523     // private
32524     showEl : function(){
32525         this.proxy.hide();
32526         this.el.setXY(this.xy);
32527         this.el.show();
32528         this.adjustAssets(true);
32529         this.toFront();
32530         this.focus();
32531         // IE peekaboo bug - fix found by Dave Fenwick
32532         if(Roo.isIE){
32533             this.el.repaint();
32534         }
32535         this.fireEvent("show", this);
32536     },
32537
32538     /**
32539      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32540      * dialog itself will receive focus.
32541      */
32542     focus : function(){
32543         if(this.defaultButton){
32544             this.defaultButton.focus();
32545         }else{
32546             this.focusEl.focus();
32547         }
32548     },
32549
32550     // private
32551     constrainXY : function(){
32552         if(this.constraintoviewport !== false){
32553             if(!this.viewSize){
32554                 if(this.container){
32555                     var s = this.container.getSize();
32556                     this.viewSize = [s.width, s.height];
32557                 }else{
32558                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32559                 }
32560             }
32561             var s = Roo.get(this.container||document).getScroll();
32562
32563             var x = this.xy[0], y = this.xy[1];
32564             var w = this.size.width, h = this.size.height;
32565             var vw = this.viewSize[0], vh = this.viewSize[1];
32566             // only move it if it needs it
32567             var moved = false;
32568             // first validate right/bottom
32569             if(x + w > vw+s.left){
32570                 x = vw - w;
32571                 moved = true;
32572             }
32573             if(y + h > vh+s.top){
32574                 y = vh - h;
32575                 moved = true;
32576             }
32577             // then make sure top/left isn't negative
32578             if(x < s.left){
32579                 x = s.left;
32580                 moved = true;
32581             }
32582             if(y < s.top){
32583                 y = s.top;
32584                 moved = true;
32585             }
32586             if(moved){
32587                 // cache xy
32588                 this.xy = [x, y];
32589                 if(this.isVisible()){
32590                     this.el.setLocation(x, y);
32591                     this.adjustAssets();
32592                 }
32593             }
32594         }
32595     },
32596
32597     // private
32598     onDrag : function(){
32599         if(!this.proxyDrag){
32600             this.xy = this.el.getXY();
32601             this.adjustAssets();
32602         }
32603     },
32604
32605     // private
32606     adjustAssets : function(doShow){
32607         var x = this.xy[0], y = this.xy[1];
32608         var w = this.size.width, h = this.size.height;
32609         if(doShow === true){
32610             if(this.shadow){
32611                 this.shadow.show(this.el);
32612             }
32613             if(this.shim){
32614                 this.shim.show();
32615             }
32616         }
32617         if(this.shadow && this.shadow.isVisible()){
32618             this.shadow.show(this.el);
32619         }
32620         if(this.shim && this.shim.isVisible()){
32621             this.shim.setBounds(x, y, w, h);
32622         }
32623     },
32624
32625     // private
32626     adjustViewport : function(w, h){
32627         if(!w || !h){
32628             w = Roo.lib.Dom.getViewWidth();
32629             h = Roo.lib.Dom.getViewHeight();
32630         }
32631         // cache the size
32632         this.viewSize = [w, h];
32633         if(this.modal && this.mask.isVisible()){
32634             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32635             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32636         }
32637         if(this.isVisible()){
32638             this.constrainXY();
32639         }
32640     },
32641
32642     /**
32643      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32644      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32645      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32646      */
32647     destroy : function(removeEl){
32648         if(this.isVisible()){
32649             this.animateTarget = null;
32650             this.hide();
32651         }
32652         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32653         if(this.tabs){
32654             this.tabs.destroy(removeEl);
32655         }
32656         Roo.destroy(
32657              this.shim,
32658              this.proxy,
32659              this.resizer,
32660              this.close,
32661              this.mask
32662         );
32663         if(this.dd){
32664             this.dd.unreg();
32665         }
32666         if(this.buttons){
32667            for(var i = 0, len = this.buttons.length; i < len; i++){
32668                this.buttons[i].destroy();
32669            }
32670         }
32671         this.el.removeAllListeners();
32672         if(removeEl === true){
32673             this.el.update("");
32674             this.el.remove();
32675         }
32676         Roo.DialogManager.unregister(this);
32677     },
32678
32679     // private
32680     startMove : function(){
32681         if(this.proxyDrag){
32682             this.proxy.show();
32683         }
32684         if(this.constraintoviewport !== false){
32685             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32686         }
32687     },
32688
32689     // private
32690     endMove : function(){
32691         if(!this.proxyDrag){
32692             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32693         }else{
32694             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32695             this.proxy.hide();
32696         }
32697         this.refreshSize();
32698         this.adjustAssets();
32699         this.focus();
32700         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32701     },
32702
32703     /**
32704      * Brings this dialog to the front of any other visible dialogs
32705      * @return {Roo.BasicDialog} this
32706      */
32707     toFront : function(){
32708         Roo.DialogManager.bringToFront(this);
32709         return this;
32710     },
32711
32712     /**
32713      * Sends this dialog to the back (under) of any other visible dialogs
32714      * @return {Roo.BasicDialog} this
32715      */
32716     toBack : function(){
32717         Roo.DialogManager.sendToBack(this);
32718         return this;
32719     },
32720
32721     /**
32722      * Centers this dialog in the viewport
32723      * @return {Roo.BasicDialog} this
32724      */
32725     center : function(){
32726         var xy = this.el.getCenterXY(true);
32727         this.moveTo(xy[0], xy[1]);
32728         return this;
32729     },
32730
32731     /**
32732      * Moves the dialog's top-left corner to the specified point
32733      * @param {Number} x
32734      * @param {Number} y
32735      * @return {Roo.BasicDialog} this
32736      */
32737     moveTo : function(x, y){
32738         this.xy = [x,y];
32739         if(this.isVisible()){
32740             this.el.setXY(this.xy);
32741             this.adjustAssets();
32742         }
32743         return this;
32744     },
32745
32746     /**
32747      * Aligns the dialog to the specified element
32748      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32749      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32750      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32751      * @return {Roo.BasicDialog} this
32752      */
32753     alignTo : function(element, position, offsets){
32754         this.xy = this.el.getAlignToXY(element, position, offsets);
32755         if(this.isVisible()){
32756             this.el.setXY(this.xy);
32757             this.adjustAssets();
32758         }
32759         return this;
32760     },
32761
32762     /**
32763      * Anchors an element to another element and realigns it when the window is resized.
32764      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32765      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32766      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32767      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32768      * is a number, it is used as the buffer delay (defaults to 50ms).
32769      * @return {Roo.BasicDialog} this
32770      */
32771     anchorTo : function(el, alignment, offsets, monitorScroll){
32772         var action = function(){
32773             this.alignTo(el, alignment, offsets);
32774         };
32775         Roo.EventManager.onWindowResize(action, this);
32776         var tm = typeof monitorScroll;
32777         if(tm != 'undefined'){
32778             Roo.EventManager.on(window, 'scroll', action, this,
32779                 {buffer: tm == 'number' ? monitorScroll : 50});
32780         }
32781         action.call(this);
32782         return this;
32783     },
32784
32785     /**
32786      * Returns true if the dialog is visible
32787      * @return {Boolean}
32788      */
32789     isVisible : function(){
32790         return this.el.isVisible();
32791     },
32792
32793     // private
32794     animHide : function(callback){
32795         var b = Roo.get(this.animateTarget).getBox();
32796         this.proxy.show();
32797         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32798         this.el.hide();
32799         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32800                     this.hideEl.createDelegate(this, [callback]));
32801     },
32802
32803     /**
32804      * Hides the dialog.
32805      * @param {Function} callback (optional) Function to call when the dialog is hidden
32806      * @return {Roo.BasicDialog} this
32807      */
32808     hide : function(callback){
32809         if (this.fireEvent("beforehide", this) === false){
32810             return;
32811         }
32812         if(this.shadow){
32813             this.shadow.hide();
32814         }
32815         if(this.shim) {
32816           this.shim.hide();
32817         }
32818         // sometimes animateTarget seems to get set.. causing problems...
32819         // this just double checks..
32820         if(this.animateTarget && Roo.get(this.animateTarget)) {
32821            this.animHide(callback);
32822         }else{
32823             this.el.hide();
32824             this.hideEl(callback);
32825         }
32826         return this;
32827     },
32828
32829     // private
32830     hideEl : function(callback){
32831         this.proxy.hide();
32832         if(this.modal){
32833             this.mask.hide();
32834             Roo.get(document.body).removeClass("x-body-masked");
32835         }
32836         this.fireEvent("hide", this);
32837         if(typeof callback == "function"){
32838             callback();
32839         }
32840     },
32841
32842     // private
32843     hideAction : function(){
32844         this.setLeft("-10000px");
32845         this.setTop("-10000px");
32846         this.setStyle("visibility", "hidden");
32847     },
32848
32849     // private
32850     refreshSize : function(){
32851         this.size = this.el.getSize();
32852         this.xy = this.el.getXY();
32853         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32854     },
32855
32856     // private
32857     // z-index is managed by the DialogManager and may be overwritten at any time
32858     setZIndex : function(index){
32859         if(this.modal){
32860             this.mask.setStyle("z-index", index);
32861         }
32862         if(this.shim){
32863             this.shim.setStyle("z-index", ++index);
32864         }
32865         if(this.shadow){
32866             this.shadow.setZIndex(++index);
32867         }
32868         this.el.setStyle("z-index", ++index);
32869         if(this.proxy){
32870             this.proxy.setStyle("z-index", ++index);
32871         }
32872         if(this.resizer){
32873             this.resizer.proxy.setStyle("z-index", ++index);
32874         }
32875
32876         this.lastZIndex = index;
32877     },
32878
32879     /**
32880      * Returns the element for this dialog
32881      * @return {Roo.Element} The underlying dialog Element
32882      */
32883     getEl : function(){
32884         return this.el;
32885     }
32886 });
32887
32888 /**
32889  * @class Roo.DialogManager
32890  * Provides global access to BasicDialogs that have been created and
32891  * support for z-indexing (layering) multiple open dialogs.
32892  */
32893 Roo.DialogManager = function(){
32894     var list = {};
32895     var accessList = [];
32896     var front = null;
32897
32898     // private
32899     var sortDialogs = function(d1, d2){
32900         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32901     };
32902
32903     // private
32904     var orderDialogs = function(){
32905         accessList.sort(sortDialogs);
32906         var seed = Roo.DialogManager.zseed;
32907         for(var i = 0, len = accessList.length; i < len; i++){
32908             var dlg = accessList[i];
32909             if(dlg){
32910                 dlg.setZIndex(seed + (i*10));
32911             }
32912         }
32913     };
32914
32915     return {
32916         /**
32917          * The starting z-index for BasicDialogs (defaults to 9000)
32918          * @type Number The z-index value
32919          */
32920         zseed : 9000,
32921
32922         // private
32923         register : function(dlg){
32924             list[dlg.id] = dlg;
32925             accessList.push(dlg);
32926         },
32927
32928         // private
32929         unregister : function(dlg){
32930             delete list[dlg.id];
32931             var i=0;
32932             var len=0;
32933             if(!accessList.indexOf){
32934                 for(  i = 0, len = accessList.length; i < len; i++){
32935                     if(accessList[i] == dlg){
32936                         accessList.splice(i, 1);
32937                         return;
32938                     }
32939                 }
32940             }else{
32941                  i = accessList.indexOf(dlg);
32942                 if(i != -1){
32943                     accessList.splice(i, 1);
32944                 }
32945             }
32946         },
32947
32948         /**
32949          * Gets a registered dialog by id
32950          * @param {String/Object} id The id of the dialog or a dialog
32951          * @return {Roo.BasicDialog} this
32952          */
32953         get : function(id){
32954             return typeof id == "object" ? id : list[id];
32955         },
32956
32957         /**
32958          * Brings the specified dialog to the front
32959          * @param {String/Object} dlg The id of the dialog or a dialog
32960          * @return {Roo.BasicDialog} this
32961          */
32962         bringToFront : function(dlg){
32963             dlg = this.get(dlg);
32964             if(dlg != front){
32965                 front = dlg;
32966                 dlg._lastAccess = new Date().getTime();
32967                 orderDialogs();
32968             }
32969             return dlg;
32970         },
32971
32972         /**
32973          * Sends the specified dialog to the back
32974          * @param {String/Object} dlg The id of the dialog or a dialog
32975          * @return {Roo.BasicDialog} this
32976          */
32977         sendToBack : function(dlg){
32978             dlg = this.get(dlg);
32979             dlg._lastAccess = -(new Date().getTime());
32980             orderDialogs();
32981             return dlg;
32982         },
32983
32984         /**
32985          * Hides all dialogs
32986          */
32987         hideAll : function(){
32988             for(var id in list){
32989                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32990                     list[id].hide();
32991                 }
32992             }
32993         }
32994     };
32995 }();
32996
32997 /**
32998  * @class Roo.LayoutDialog
32999  * @extends Roo.BasicDialog
33000  * Dialog which provides adjustments for working with a layout in a Dialog.
33001  * Add your necessary layout config options to the dialog's config.<br>
33002  * Example usage (including a nested layout):
33003  * <pre><code>
33004 if(!dialog){
33005     dialog = new Roo.LayoutDialog("download-dlg", {
33006         modal: true,
33007         width:600,
33008         height:450,
33009         shadow:true,
33010         minWidth:500,
33011         minHeight:350,
33012         autoTabs:true,
33013         proxyDrag:true,
33014         // layout config merges with the dialog config
33015         center:{
33016             tabPosition: "top",
33017             alwaysShowTabs: true
33018         }
33019     });
33020     dialog.addKeyListener(27, dialog.hide, dialog);
33021     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33022     dialog.addButton("Build It!", this.getDownload, this);
33023
33024     // we can even add nested layouts
33025     var innerLayout = new Roo.BorderLayout("dl-inner", {
33026         east: {
33027             initialSize: 200,
33028             autoScroll:true,
33029             split:true
33030         },
33031         center: {
33032             autoScroll:true
33033         }
33034     });
33035     innerLayout.beginUpdate();
33036     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33037     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33038     innerLayout.endUpdate(true);
33039
33040     var layout = dialog.getLayout();
33041     layout.beginUpdate();
33042     layout.add("center", new Roo.ContentPanel("standard-panel",
33043                         {title: "Download the Source", fitToFrame:true}));
33044     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33045                {title: "Build your own roo.js"}));
33046     layout.getRegion("center").showPanel(sp);
33047     layout.endUpdate();
33048 }
33049 </code></pre>
33050     * @constructor
33051     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33052     * @param {Object} config configuration options
33053   */
33054 Roo.LayoutDialog = function(el, cfg){
33055     
33056     var config=  cfg;
33057     if (typeof(cfg) == 'undefined') {
33058         config = Roo.apply({}, el);
33059         // not sure why we use documentElement here.. - it should always be body.
33060         // IE7 borks horribly if we use documentElement.
33061         // webkit also does not like documentElement - it creates a body element...
33062         el = Roo.get( document.body || document.documentElement ).createChild();
33063         //config.autoCreate = true;
33064     }
33065     
33066     
33067     config.autoTabs = false;
33068     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33069     this.body.setStyle({overflow:"hidden", position:"relative"});
33070     this.layout = new Roo.BorderLayout(this.body.dom, config);
33071     this.layout.monitorWindowResize = false;
33072     this.el.addClass("x-dlg-auto-layout");
33073     // fix case when center region overwrites center function
33074     this.center = Roo.BasicDialog.prototype.center;
33075     this.on("show", this.layout.layout, this.layout, true);
33076     if (config.items) {
33077         var xitems = config.items;
33078         delete config.items;
33079         Roo.each(xitems, this.addxtype, this);
33080     }
33081     
33082     
33083 };
33084 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33085     /**
33086      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33087      * @deprecated
33088      */
33089     endUpdate : function(){
33090         this.layout.endUpdate();
33091     },
33092
33093     /**
33094      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33095      *  @deprecated
33096      */
33097     beginUpdate : function(){
33098         this.layout.beginUpdate();
33099     },
33100
33101     /**
33102      * Get the BorderLayout for this dialog
33103      * @return {Roo.BorderLayout}
33104      */
33105     getLayout : function(){
33106         return this.layout;
33107     },
33108
33109     showEl : function(){
33110         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33111         if(Roo.isIE7){
33112             this.layout.layout();
33113         }
33114     },
33115
33116     // private
33117     // Use the syncHeightBeforeShow config option to control this automatically
33118     syncBodyHeight : function(){
33119         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33120         if(this.layout){this.layout.layout();}
33121     },
33122     
33123       /**
33124      * Add an xtype element (actually adds to the layout.)
33125      * @return {Object} xdata xtype object data.
33126      */
33127     
33128     addxtype : function(c) {
33129         return this.layout.addxtype(c);
33130     }
33131 });/*
33132  * Based on:
33133  * Ext JS Library 1.1.1
33134  * Copyright(c) 2006-2007, Ext JS, LLC.
33135  *
33136  * Originally Released Under LGPL - original licence link has changed is not relivant.
33137  *
33138  * Fork - LGPL
33139  * <script type="text/javascript">
33140  */
33141  
33142 /**
33143  * @class Roo.MessageBox
33144  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33145  * Example usage:
33146  *<pre><code>
33147 // Basic alert:
33148 Roo.Msg.alert('Status', 'Changes saved successfully.');
33149
33150 // Prompt for user data:
33151 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33152     if (btn == 'ok'){
33153         // process text value...
33154     }
33155 });
33156
33157 // Show a dialog using config options:
33158 Roo.Msg.show({
33159    title:'Save Changes?',
33160    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33161    buttons: Roo.Msg.YESNOCANCEL,
33162    fn: processResult,
33163    animEl: 'elId'
33164 });
33165 </code></pre>
33166  * @singleton
33167  */
33168 Roo.MessageBox = function(){
33169     var dlg, opt, mask, waitTimer;
33170     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33171     var buttons, activeTextEl, bwidth;
33172
33173     // private
33174     var handleButton = function(button){
33175         dlg.hide();
33176         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33177     };
33178
33179     // private
33180     var handleHide = function(){
33181         if(opt && opt.cls){
33182             dlg.el.removeClass(opt.cls);
33183         }
33184         if(waitTimer){
33185             Roo.TaskMgr.stop(waitTimer);
33186             waitTimer = null;
33187         }
33188     };
33189
33190     // private
33191     var updateButtons = function(b){
33192         var width = 0;
33193         if(!b){
33194             buttons["ok"].hide();
33195             buttons["cancel"].hide();
33196             buttons["yes"].hide();
33197             buttons["no"].hide();
33198             dlg.footer.dom.style.display = 'none';
33199             return width;
33200         }
33201         dlg.footer.dom.style.display = '';
33202         for(var k in buttons){
33203             if(typeof buttons[k] != "function"){
33204                 if(b[k]){
33205                     buttons[k].show();
33206                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33207                     width += buttons[k].el.getWidth()+15;
33208                 }else{
33209                     buttons[k].hide();
33210                 }
33211             }
33212         }
33213         return width;
33214     };
33215
33216     // private
33217     var handleEsc = function(d, k, e){
33218         if(opt && opt.closable !== false){
33219             dlg.hide();
33220         }
33221         if(e){
33222             e.stopEvent();
33223         }
33224     };
33225
33226     return {
33227         /**
33228          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33229          * @return {Roo.BasicDialog} The BasicDialog element
33230          */
33231         getDialog : function(){
33232            if(!dlg){
33233                 dlg = new Roo.BasicDialog("x-msg-box", {
33234                     autoCreate : true,
33235                     shadow: true,
33236                     draggable: true,
33237                     resizable:false,
33238                     constraintoviewport:false,
33239                     fixedcenter:true,
33240                     collapsible : false,
33241                     shim:true,
33242                     modal: true,
33243                     width:400, height:100,
33244                     buttonAlign:"center",
33245                     closeClick : function(){
33246                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33247                             handleButton("no");
33248                         }else{
33249                             handleButton("cancel");
33250                         }
33251                     }
33252                 });
33253                 dlg.on("hide", handleHide);
33254                 mask = dlg.mask;
33255                 dlg.addKeyListener(27, handleEsc);
33256                 buttons = {};
33257                 var bt = this.buttonText;
33258                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33259                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33260                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33261                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33262                 bodyEl = dlg.body.createChild({
33263
33264                     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>'
33265                 });
33266                 msgEl = bodyEl.dom.firstChild;
33267                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33268                 textboxEl.enableDisplayMode();
33269                 textboxEl.addKeyListener([10,13], function(){
33270                     if(dlg.isVisible() && opt && opt.buttons){
33271                         if(opt.buttons.ok){
33272                             handleButton("ok");
33273                         }else if(opt.buttons.yes){
33274                             handleButton("yes");
33275                         }
33276                     }
33277                 });
33278                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33279                 textareaEl.enableDisplayMode();
33280                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33281                 progressEl.enableDisplayMode();
33282                 var pf = progressEl.dom.firstChild;
33283                 if (pf) {
33284                     pp = Roo.get(pf.firstChild);
33285                     pp.setHeight(pf.offsetHeight);
33286                 }
33287                 
33288             }
33289             return dlg;
33290         },
33291
33292         /**
33293          * Updates the message box body text
33294          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33295          * the XHTML-compliant non-breaking space character '&amp;#160;')
33296          * @return {Roo.MessageBox} This message box
33297          */
33298         updateText : function(text){
33299             if(!dlg.isVisible() && !opt.width){
33300                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33301             }
33302             msgEl.innerHTML = text || '&#160;';
33303       
33304             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33305             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33306             var w = Math.max(
33307                     Math.min(opt.width || cw , this.maxWidth), 
33308                     Math.max(opt.minWidth || this.minWidth, bwidth)
33309             );
33310             if(opt.prompt){
33311                 activeTextEl.setWidth(w);
33312             }
33313             if(dlg.isVisible()){
33314                 dlg.fixedcenter = false;
33315             }
33316             // to big, make it scroll. = But as usual stupid IE does not support
33317             // !important..
33318             
33319             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33320                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33321                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33322             } else {
33323                 bodyEl.dom.style.height = '';
33324                 bodyEl.dom.style.overflowY = '';
33325             }
33326             if (cw > w) {
33327                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33328             } else {
33329                 bodyEl.dom.style.overflowX = '';
33330             }
33331             
33332             dlg.setContentSize(w, bodyEl.getHeight());
33333             if(dlg.isVisible()){
33334                 dlg.fixedcenter = true;
33335             }
33336             return this;
33337         },
33338
33339         /**
33340          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33341          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33342          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33343          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33344          * @return {Roo.MessageBox} This message box
33345          */
33346         updateProgress : function(value, text){
33347             if(text){
33348                 this.updateText(text);
33349             }
33350             if (pp) { // weird bug on my firefox - for some reason this is not defined
33351                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33352             }
33353             return this;
33354         },        
33355
33356         /**
33357          * Returns true if the message box is currently displayed
33358          * @return {Boolean} True if the message box is visible, else false
33359          */
33360         isVisible : function(){
33361             return dlg && dlg.isVisible();  
33362         },
33363
33364         /**
33365          * Hides the message box if it is displayed
33366          */
33367         hide : function(){
33368             if(this.isVisible()){
33369                 dlg.hide();
33370             }  
33371         },
33372
33373         /**
33374          * Displays a new message box, or reinitializes an existing message box, based on the config options
33375          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33376          * The following config object properties are supported:
33377          * <pre>
33378 Property    Type             Description
33379 ----------  ---------------  ------------------------------------------------------------------------------------
33380 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33381                                    closes (defaults to undefined)
33382 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33383                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33384 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33385                                    progress and wait dialogs will ignore this property and always hide the
33386                                    close button as they can only be closed programmatically.
33387 cls               String           A custom CSS class to apply to the message box element
33388 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33389                                    displayed (defaults to 75)
33390 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33391                                    function will be btn (the name of the button that was clicked, if applicable,
33392                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33393                                    Progress and wait dialogs will ignore this option since they do not respond to
33394                                    user actions and can only be closed programmatically, so any required function
33395                                    should be called by the same code after it closes the dialog.
33396 icon              String           A CSS class that provides a background image to be used as an icon for
33397                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33398 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33399 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33400 modal             Boolean          False to allow user interaction with the page while the message box is
33401                                    displayed (defaults to true)
33402 msg               String           A string that will replace the existing message box body text (defaults
33403                                    to the XHTML-compliant non-breaking space character '&#160;')
33404 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33405 progress          Boolean          True to display a progress bar (defaults to false)
33406 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33407 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33408 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33409 title             String           The title text
33410 value             String           The string value to set into the active textbox element if displayed
33411 wait              Boolean          True to display a progress bar (defaults to false)
33412 width             Number           The width of the dialog in pixels
33413 </pre>
33414          *
33415          * Example usage:
33416          * <pre><code>
33417 Roo.Msg.show({
33418    title: 'Address',
33419    msg: 'Please enter your address:',
33420    width: 300,
33421    buttons: Roo.MessageBox.OKCANCEL,
33422    multiline: true,
33423    fn: saveAddress,
33424    animEl: 'addAddressBtn'
33425 });
33426 </code></pre>
33427          * @param {Object} config Configuration options
33428          * @return {Roo.MessageBox} This message box
33429          */
33430         show : function(options)
33431         {
33432             
33433             // this causes nightmares if you show one dialog after another
33434             // especially on callbacks..
33435              
33436             if(this.isVisible()){
33437                 
33438                 this.hide();
33439                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33440                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33441                 Roo.log("New Dialog Message:" +  options.msg )
33442                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33443                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33444                 
33445             }
33446             var d = this.getDialog();
33447             opt = options;
33448             d.setTitle(opt.title || "&#160;");
33449             d.close.setDisplayed(opt.closable !== false);
33450             activeTextEl = textboxEl;
33451             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33452             if(opt.prompt){
33453                 if(opt.multiline){
33454                     textboxEl.hide();
33455                     textareaEl.show();
33456                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33457                         opt.multiline : this.defaultTextHeight);
33458                     activeTextEl = textareaEl;
33459                 }else{
33460                     textboxEl.show();
33461                     textareaEl.hide();
33462                 }
33463             }else{
33464                 textboxEl.hide();
33465                 textareaEl.hide();
33466             }
33467             progressEl.setDisplayed(opt.progress === true);
33468             this.updateProgress(0);
33469             activeTextEl.dom.value = opt.value || "";
33470             if(opt.prompt){
33471                 dlg.setDefaultButton(activeTextEl);
33472             }else{
33473                 var bs = opt.buttons;
33474                 var db = null;
33475                 if(bs && bs.ok){
33476                     db = buttons["ok"];
33477                 }else if(bs && bs.yes){
33478                     db = buttons["yes"];
33479                 }
33480                 dlg.setDefaultButton(db);
33481             }
33482             bwidth = updateButtons(opt.buttons);
33483             this.updateText(opt.msg);
33484             if(opt.cls){
33485                 d.el.addClass(opt.cls);
33486             }
33487             d.proxyDrag = opt.proxyDrag === true;
33488             d.modal = opt.modal !== false;
33489             d.mask = opt.modal !== false ? mask : false;
33490             if(!d.isVisible()){
33491                 // force it to the end of the z-index stack so it gets a cursor in FF
33492                 document.body.appendChild(dlg.el.dom);
33493                 d.animateTarget = null;
33494                 d.show(options.animEl);
33495             }
33496             return this;
33497         },
33498
33499         /**
33500          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33501          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33502          * and closing the message box when the process is complete.
33503          * @param {String} title The title bar text
33504          * @param {String} msg The message box body text
33505          * @return {Roo.MessageBox} This message box
33506          */
33507         progress : function(title, msg){
33508             this.show({
33509                 title : title,
33510                 msg : msg,
33511                 buttons: false,
33512                 progress:true,
33513                 closable:false,
33514                 minWidth: this.minProgressWidth,
33515                 modal : true
33516             });
33517             return this;
33518         },
33519
33520         /**
33521          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33522          * If a callback function is passed it will be called after the user clicks the button, and the
33523          * id of the button that was clicked will be passed as the only parameter to the callback
33524          * (could also be the top-right close button).
33525          * @param {String} title The title bar text
33526          * @param {String} msg The message box body text
33527          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33528          * @param {Object} scope (optional) The scope of the callback function
33529          * @return {Roo.MessageBox} This message box
33530          */
33531         alert : function(title, msg, fn, scope){
33532             this.show({
33533                 title : title,
33534                 msg : msg,
33535                 buttons: this.OK,
33536                 fn: fn,
33537                 scope : scope,
33538                 modal : true
33539             });
33540             return this;
33541         },
33542
33543         /**
33544          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33545          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33546          * You are responsible for closing the message box when the process is complete.
33547          * @param {String} msg The message box body text
33548          * @param {String} title (optional) The title bar text
33549          * @return {Roo.MessageBox} This message box
33550          */
33551         wait : function(msg, title){
33552             this.show({
33553                 title : title,
33554                 msg : msg,
33555                 buttons: false,
33556                 closable:false,
33557                 progress:true,
33558                 modal:true,
33559                 width:300,
33560                 wait:true
33561             });
33562             waitTimer = Roo.TaskMgr.start({
33563                 run: function(i){
33564                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33565                 },
33566                 interval: 1000
33567             });
33568             return this;
33569         },
33570
33571         /**
33572          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33573          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33574          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33575          * @param {String} title The title bar text
33576          * @param {String} msg The message box body text
33577          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33578          * @param {Object} scope (optional) The scope of the callback function
33579          * @return {Roo.MessageBox} This message box
33580          */
33581         confirm : function(title, msg, fn, scope){
33582             this.show({
33583                 title : title,
33584                 msg : msg,
33585                 buttons: this.YESNO,
33586                 fn: fn,
33587                 scope : scope,
33588                 modal : true
33589             });
33590             return this;
33591         },
33592
33593         /**
33594          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33595          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33596          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33597          * (could also be the top-right close button) and the text that was entered will be passed as the two
33598          * parameters to the callback.
33599          * @param {String} title The title bar text
33600          * @param {String} msg The message box body text
33601          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33602          * @param {Object} scope (optional) The scope of the callback function
33603          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33604          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33605          * @return {Roo.MessageBox} This message box
33606          */
33607         prompt : function(title, msg, fn, scope, multiline){
33608             this.show({
33609                 title : title,
33610                 msg : msg,
33611                 buttons: this.OKCANCEL,
33612                 fn: fn,
33613                 minWidth:250,
33614                 scope : scope,
33615                 prompt:true,
33616                 multiline: multiline,
33617                 modal : true
33618             });
33619             return this;
33620         },
33621
33622         /**
33623          * Button config that displays a single OK button
33624          * @type Object
33625          */
33626         OK : {ok:true},
33627         /**
33628          * Button config that displays Yes and No buttons
33629          * @type Object
33630          */
33631         YESNO : {yes:true, no:true},
33632         /**
33633          * Button config that displays OK and Cancel buttons
33634          * @type Object
33635          */
33636         OKCANCEL : {ok:true, cancel:true},
33637         /**
33638          * Button config that displays Yes, No and Cancel buttons
33639          * @type Object
33640          */
33641         YESNOCANCEL : {yes:true, no:true, cancel:true},
33642
33643         /**
33644          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33645          * @type Number
33646          */
33647         defaultTextHeight : 75,
33648         /**
33649          * The maximum width in pixels of the message box (defaults to 600)
33650          * @type Number
33651          */
33652         maxWidth : 600,
33653         /**
33654          * The minimum width in pixels of the message box (defaults to 100)
33655          * @type Number
33656          */
33657         minWidth : 100,
33658         /**
33659          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33660          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33661          * @type Number
33662          */
33663         minProgressWidth : 250,
33664         /**
33665          * An object containing the default button text strings that can be overriden for localized language support.
33666          * Supported properties are: ok, cancel, yes and no.
33667          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33668          * @type Object
33669          */
33670         buttonText : {
33671             ok : "OK",
33672             cancel : "Cancel",
33673             yes : "Yes",
33674             no : "No"
33675         }
33676     };
33677 }();
33678
33679 /**
33680  * Shorthand for {@link Roo.MessageBox}
33681  */
33682 Roo.Msg = Roo.MessageBox;/*
33683  * Based on:
33684  * Ext JS Library 1.1.1
33685  * Copyright(c) 2006-2007, Ext JS, LLC.
33686  *
33687  * Originally Released Under LGPL - original licence link has changed is not relivant.
33688  *
33689  * Fork - LGPL
33690  * <script type="text/javascript">
33691  */
33692 /**
33693  * @class Roo.QuickTips
33694  * Provides attractive and customizable tooltips for any element.
33695  * @singleton
33696  */
33697 Roo.QuickTips = function(){
33698     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33699     var ce, bd, xy, dd;
33700     var visible = false, disabled = true, inited = false;
33701     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33702     
33703     var onOver = function(e){
33704         if(disabled){
33705             return;
33706         }
33707         var t = e.getTarget();
33708         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33709             return;
33710         }
33711         if(ce && t == ce.el){
33712             clearTimeout(hideProc);
33713             return;
33714         }
33715         if(t && tagEls[t.id]){
33716             tagEls[t.id].el = t;
33717             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33718             return;
33719         }
33720         var ttp, et = Roo.fly(t);
33721         var ns = cfg.namespace;
33722         if(tm.interceptTitles && t.title){
33723             ttp = t.title;
33724             t.qtip = ttp;
33725             t.removeAttribute("title");
33726             e.preventDefault();
33727         }else{
33728             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33729         }
33730         if(ttp){
33731             showProc = show.defer(tm.showDelay, tm, [{
33732                 el: t, 
33733                 text: ttp.replace(/\\n/g,'<br/>'),
33734                 width: et.getAttributeNS(ns, cfg.width),
33735                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33736                 title: et.getAttributeNS(ns, cfg.title),
33737                     cls: et.getAttributeNS(ns, cfg.cls)
33738             }]);
33739         }
33740     };
33741     
33742     var onOut = function(e){
33743         clearTimeout(showProc);
33744         var t = e.getTarget();
33745         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33746             hideProc = setTimeout(hide, tm.hideDelay);
33747         }
33748     };
33749     
33750     var onMove = function(e){
33751         if(disabled){
33752             return;
33753         }
33754         xy = e.getXY();
33755         xy[1] += 18;
33756         if(tm.trackMouse && ce){
33757             el.setXY(xy);
33758         }
33759     };
33760     
33761     var onDown = function(e){
33762         clearTimeout(showProc);
33763         clearTimeout(hideProc);
33764         if(!e.within(el)){
33765             if(tm.hideOnClick){
33766                 hide();
33767                 tm.disable();
33768                 tm.enable.defer(100, tm);
33769             }
33770         }
33771     };
33772     
33773     var getPad = function(){
33774         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33775     };
33776
33777     var show = function(o){
33778         if(disabled){
33779             return;
33780         }
33781         clearTimeout(dismissProc);
33782         ce = o;
33783         if(removeCls){ // in case manually hidden
33784             el.removeClass(removeCls);
33785             removeCls = null;
33786         }
33787         if(ce.cls){
33788             el.addClass(ce.cls);
33789             removeCls = ce.cls;
33790         }
33791         if(ce.title){
33792             tipTitle.update(ce.title);
33793             tipTitle.show();
33794         }else{
33795             tipTitle.update('');
33796             tipTitle.hide();
33797         }
33798         el.dom.style.width  = tm.maxWidth+'px';
33799         //tipBody.dom.style.width = '';
33800         tipBodyText.update(o.text);
33801         var p = getPad(), w = ce.width;
33802         if(!w){
33803             var td = tipBodyText.dom;
33804             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33805             if(aw > tm.maxWidth){
33806                 w = tm.maxWidth;
33807             }else if(aw < tm.minWidth){
33808                 w = tm.minWidth;
33809             }else{
33810                 w = aw;
33811             }
33812         }
33813         //tipBody.setWidth(w);
33814         el.setWidth(parseInt(w, 10) + p);
33815         if(ce.autoHide === false){
33816             close.setDisplayed(true);
33817             if(dd){
33818                 dd.unlock();
33819             }
33820         }else{
33821             close.setDisplayed(false);
33822             if(dd){
33823                 dd.lock();
33824             }
33825         }
33826         if(xy){
33827             el.avoidY = xy[1]-18;
33828             el.setXY(xy);
33829         }
33830         if(tm.animate){
33831             el.setOpacity(.1);
33832             el.setStyle("visibility", "visible");
33833             el.fadeIn({callback: afterShow});
33834         }else{
33835             afterShow();
33836         }
33837     };
33838     
33839     var afterShow = function(){
33840         if(ce){
33841             el.show();
33842             esc.enable();
33843             if(tm.autoDismiss && ce.autoHide !== false){
33844                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33845             }
33846         }
33847     };
33848     
33849     var hide = function(noanim){
33850         clearTimeout(dismissProc);
33851         clearTimeout(hideProc);
33852         ce = null;
33853         if(el.isVisible()){
33854             esc.disable();
33855             if(noanim !== true && tm.animate){
33856                 el.fadeOut({callback: afterHide});
33857             }else{
33858                 afterHide();
33859             } 
33860         }
33861     };
33862     
33863     var afterHide = function(){
33864         el.hide();
33865         if(removeCls){
33866             el.removeClass(removeCls);
33867             removeCls = null;
33868         }
33869     };
33870     
33871     return {
33872         /**
33873         * @cfg {Number} minWidth
33874         * The minimum width of the quick tip (defaults to 40)
33875         */
33876        minWidth : 40,
33877         /**
33878         * @cfg {Number} maxWidth
33879         * The maximum width of the quick tip (defaults to 300)
33880         */
33881        maxWidth : 300,
33882         /**
33883         * @cfg {Boolean} interceptTitles
33884         * True to automatically use the element's DOM title value if available (defaults to false)
33885         */
33886        interceptTitles : false,
33887         /**
33888         * @cfg {Boolean} trackMouse
33889         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33890         */
33891        trackMouse : false,
33892         /**
33893         * @cfg {Boolean} hideOnClick
33894         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33895         */
33896        hideOnClick : true,
33897         /**
33898         * @cfg {Number} showDelay
33899         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33900         */
33901        showDelay : 500,
33902         /**
33903         * @cfg {Number} hideDelay
33904         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33905         */
33906        hideDelay : 200,
33907         /**
33908         * @cfg {Boolean} autoHide
33909         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33910         * Used in conjunction with hideDelay.
33911         */
33912        autoHide : true,
33913         /**
33914         * @cfg {Boolean}
33915         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33916         * (defaults to true).  Used in conjunction with autoDismissDelay.
33917         */
33918        autoDismiss : true,
33919         /**
33920         * @cfg {Number}
33921         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33922         */
33923        autoDismissDelay : 5000,
33924        /**
33925         * @cfg {Boolean} animate
33926         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33927         */
33928        animate : false,
33929
33930        /**
33931         * @cfg {String} title
33932         * Title text to display (defaults to '').  This can be any valid HTML markup.
33933         */
33934         title: '',
33935        /**
33936         * @cfg {String} text
33937         * Body text to display (defaults to '').  This can be any valid HTML markup.
33938         */
33939         text : '',
33940        /**
33941         * @cfg {String} cls
33942         * A CSS class to apply to the base quick tip element (defaults to '').
33943         */
33944         cls : '',
33945        /**
33946         * @cfg {Number} width
33947         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33948         * minWidth or maxWidth.
33949         */
33950         width : null,
33951
33952     /**
33953      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33954      * or display QuickTips in a page.
33955      */
33956        init : function(){
33957           tm = Roo.QuickTips;
33958           cfg = tm.tagConfig;
33959           if(!inited){
33960               if(!Roo.isReady){ // allow calling of init() before onReady
33961                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33962                   return;
33963               }
33964               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33965               el.fxDefaults = {stopFx: true};
33966               // maximum custom styling
33967               //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>');
33968               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>');              
33969               tipTitle = el.child('h3');
33970               tipTitle.enableDisplayMode("block");
33971               tipBody = el.child('div.x-tip-bd');
33972               tipBodyText = el.child('div.x-tip-bd-inner');
33973               //bdLeft = el.child('div.x-tip-bd-left');
33974               //bdRight = el.child('div.x-tip-bd-right');
33975               close = el.child('div.x-tip-close');
33976               close.enableDisplayMode("block");
33977               close.on("click", hide);
33978               var d = Roo.get(document);
33979               d.on("mousedown", onDown);
33980               d.on("mouseover", onOver);
33981               d.on("mouseout", onOut);
33982               d.on("mousemove", onMove);
33983               esc = d.addKeyListener(27, hide);
33984               esc.disable();
33985               if(Roo.dd.DD){
33986                   dd = el.initDD("default", null, {
33987                       onDrag : function(){
33988                           el.sync();  
33989                       }
33990                   });
33991                   dd.setHandleElId(tipTitle.id);
33992                   dd.lock();
33993               }
33994               inited = true;
33995           }
33996           this.enable(); 
33997        },
33998
33999     /**
34000      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34001      * are supported:
34002      * <pre>
34003 Property    Type                   Description
34004 ----------  ---------------------  ------------------------------------------------------------------------
34005 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34006      * </ul>
34007      * @param {Object} config The config object
34008      */
34009        register : function(config){
34010            var cs = config instanceof Array ? config : arguments;
34011            for(var i = 0, len = cs.length; i < len; i++) {
34012                var c = cs[i];
34013                var target = c.target;
34014                if(target){
34015                    if(target instanceof Array){
34016                        for(var j = 0, jlen = target.length; j < jlen; j++){
34017                            tagEls[target[j]] = c;
34018                        }
34019                    }else{
34020                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34021                    }
34022                }
34023            }
34024        },
34025
34026     /**
34027      * Removes this quick tip from its element and destroys it.
34028      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34029      */
34030        unregister : function(el){
34031            delete tagEls[Roo.id(el)];
34032        },
34033
34034     /**
34035      * Enable this quick tip.
34036      */
34037        enable : function(){
34038            if(inited && disabled){
34039                locks.pop();
34040                if(locks.length < 1){
34041                    disabled = false;
34042                }
34043            }
34044        },
34045
34046     /**
34047      * Disable this quick tip.
34048      */
34049        disable : function(){
34050           disabled = true;
34051           clearTimeout(showProc);
34052           clearTimeout(hideProc);
34053           clearTimeout(dismissProc);
34054           if(ce){
34055               hide(true);
34056           }
34057           locks.push(1);
34058        },
34059
34060     /**
34061      * Returns true if the quick tip is enabled, else false.
34062      */
34063        isEnabled : function(){
34064             return !disabled;
34065        },
34066
34067         // private
34068        tagConfig : {
34069            namespace : "roo", // was ext?? this may break..
34070            alt_namespace : "ext",
34071            attribute : "qtip",
34072            width : "width",
34073            target : "target",
34074            title : "qtitle",
34075            hide : "hide",
34076            cls : "qclass"
34077        }
34078    };
34079 }();
34080
34081 // backwards compat
34082 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34083  * Based on:
34084  * Ext JS Library 1.1.1
34085  * Copyright(c) 2006-2007, Ext JS, LLC.
34086  *
34087  * Originally Released Under LGPL - original licence link has changed is not relivant.
34088  *
34089  * Fork - LGPL
34090  * <script type="text/javascript">
34091  */
34092  
34093
34094 /**
34095  * @class Roo.tree.TreePanel
34096  * @extends Roo.data.Tree
34097
34098  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34099  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34100  * @cfg {Boolean} enableDD true to enable drag and drop
34101  * @cfg {Boolean} enableDrag true to enable just drag
34102  * @cfg {Boolean} enableDrop true to enable just drop
34103  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34104  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34105  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34106  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34107  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34108  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34109  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34110  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34111  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34112  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34113  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34114  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34115  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34116  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34117  * @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>
34118  * @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>
34119  * 
34120  * @constructor
34121  * @param {String/HTMLElement/Element} el The container element
34122  * @param {Object} config
34123  */
34124 Roo.tree.TreePanel = function(el, config){
34125     var root = false;
34126     var loader = false;
34127     if (config.root) {
34128         root = config.root;
34129         delete config.root;
34130     }
34131     if (config.loader) {
34132         loader = config.loader;
34133         delete config.loader;
34134     }
34135     
34136     Roo.apply(this, config);
34137     Roo.tree.TreePanel.superclass.constructor.call(this);
34138     this.el = Roo.get(el);
34139     this.el.addClass('x-tree');
34140     //console.log(root);
34141     if (root) {
34142         this.setRootNode( Roo.factory(root, Roo.tree));
34143     }
34144     if (loader) {
34145         this.loader = Roo.factory(loader, Roo.tree);
34146     }
34147    /**
34148     * Read-only. The id of the container element becomes this TreePanel's id.
34149     */
34150     this.id = this.el.id;
34151     this.addEvents({
34152         /**
34153         * @event beforeload
34154         * Fires before a node is loaded, return false to cancel
34155         * @param {Node} node The node being loaded
34156         */
34157         "beforeload" : true,
34158         /**
34159         * @event load
34160         * Fires when a node is loaded
34161         * @param {Node} node The node that was loaded
34162         */
34163         "load" : true,
34164         /**
34165         * @event textchange
34166         * Fires when the text for a node is changed
34167         * @param {Node} node The node
34168         * @param {String} text The new text
34169         * @param {String} oldText The old text
34170         */
34171         "textchange" : true,
34172         /**
34173         * @event beforeexpand
34174         * Fires before a node is expanded, return false to cancel.
34175         * @param {Node} node The node
34176         * @param {Boolean} deep
34177         * @param {Boolean} anim
34178         */
34179         "beforeexpand" : true,
34180         /**
34181         * @event beforecollapse
34182         * Fires before a node is collapsed, return false to cancel.
34183         * @param {Node} node The node
34184         * @param {Boolean} deep
34185         * @param {Boolean} anim
34186         */
34187         "beforecollapse" : true,
34188         /**
34189         * @event expand
34190         * Fires when a node is expanded
34191         * @param {Node} node The node
34192         */
34193         "expand" : true,
34194         /**
34195         * @event disabledchange
34196         * Fires when the disabled status of a node changes
34197         * @param {Node} node The node
34198         * @param {Boolean} disabled
34199         */
34200         "disabledchange" : true,
34201         /**
34202         * @event collapse
34203         * Fires when a node is collapsed
34204         * @param {Node} node The node
34205         */
34206         "collapse" : true,
34207         /**
34208         * @event beforeclick
34209         * Fires before click processing on a node. Return false to cancel the default action.
34210         * @param {Node} node The node
34211         * @param {Roo.EventObject} e The event object
34212         */
34213         "beforeclick":true,
34214         /**
34215         * @event checkchange
34216         * Fires when a node with a checkbox's checked property changes
34217         * @param {Node} this This node
34218         * @param {Boolean} checked
34219         */
34220         "checkchange":true,
34221         /**
34222         * @event click
34223         * Fires when a node is clicked
34224         * @param {Node} node The node
34225         * @param {Roo.EventObject} e The event object
34226         */
34227         "click":true,
34228         /**
34229         * @event dblclick
34230         * Fires when a node is double clicked
34231         * @param {Node} node The node
34232         * @param {Roo.EventObject} e The event object
34233         */
34234         "dblclick":true,
34235         /**
34236         * @event contextmenu
34237         * Fires when a node is right clicked
34238         * @param {Node} node The node
34239         * @param {Roo.EventObject} e The event object
34240         */
34241         "contextmenu":true,
34242         /**
34243         * @event beforechildrenrendered
34244         * Fires right before the child nodes for a node are rendered
34245         * @param {Node} node The node
34246         */
34247         "beforechildrenrendered":true,
34248         /**
34249         * @event startdrag
34250         * Fires when a node starts being dragged
34251         * @param {Roo.tree.TreePanel} this
34252         * @param {Roo.tree.TreeNode} node
34253         * @param {event} e The raw browser event
34254         */ 
34255        "startdrag" : true,
34256        /**
34257         * @event enddrag
34258         * Fires when a drag operation is complete
34259         * @param {Roo.tree.TreePanel} this
34260         * @param {Roo.tree.TreeNode} node
34261         * @param {event} e The raw browser event
34262         */
34263        "enddrag" : true,
34264        /**
34265         * @event dragdrop
34266         * Fires when a dragged node is dropped on a valid DD target
34267         * @param {Roo.tree.TreePanel} this
34268         * @param {Roo.tree.TreeNode} node
34269         * @param {DD} dd The dd it was dropped on
34270         * @param {event} e The raw browser event
34271         */
34272        "dragdrop" : true,
34273        /**
34274         * @event beforenodedrop
34275         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34276         * passed to handlers has the following properties:<br />
34277         * <ul style="padding:5px;padding-left:16px;">
34278         * <li>tree - The TreePanel</li>
34279         * <li>target - The node being targeted for the drop</li>
34280         * <li>data - The drag data from the drag source</li>
34281         * <li>point - The point of the drop - append, above or below</li>
34282         * <li>source - The drag source</li>
34283         * <li>rawEvent - Raw mouse event</li>
34284         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34285         * to be inserted by setting them on this object.</li>
34286         * <li>cancel - Set this to true to cancel the drop.</li>
34287         * </ul>
34288         * @param {Object} dropEvent
34289         */
34290        "beforenodedrop" : true,
34291        /**
34292         * @event nodedrop
34293         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34294         * passed to handlers has the following properties:<br />
34295         * <ul style="padding:5px;padding-left:16px;">
34296         * <li>tree - The TreePanel</li>
34297         * <li>target - The node being targeted for the drop</li>
34298         * <li>data - The drag data from the drag source</li>
34299         * <li>point - The point of the drop - append, above or below</li>
34300         * <li>source - The drag source</li>
34301         * <li>rawEvent - Raw mouse event</li>
34302         * <li>dropNode - Dropped node(s).</li>
34303         * </ul>
34304         * @param {Object} dropEvent
34305         */
34306        "nodedrop" : true,
34307         /**
34308         * @event nodedragover
34309         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34310         * passed to handlers has the following properties:<br />
34311         * <ul style="padding:5px;padding-left:16px;">
34312         * <li>tree - The TreePanel</li>
34313         * <li>target - The node being targeted for the drop</li>
34314         * <li>data - The drag data from the drag source</li>
34315         * <li>point - The point of the drop - append, above or below</li>
34316         * <li>source - The drag source</li>
34317         * <li>rawEvent - Raw mouse event</li>
34318         * <li>dropNode - Drop node(s) provided by the source.</li>
34319         * <li>cancel - Set this to true to signal drop not allowed.</li>
34320         * </ul>
34321         * @param {Object} dragOverEvent
34322         */
34323        "nodedragover" : true,
34324        /**
34325         * @event appendnode
34326         * Fires when append node to the tree
34327         * @param {Roo.tree.TreePanel} this
34328         * @param {Roo.tree.TreeNode} node
34329         * @param {Number} index The index of the newly appended node
34330         */
34331        "appendnode" : true
34332         
34333     });
34334     if(this.singleExpand){
34335        this.on("beforeexpand", this.restrictExpand, this);
34336     }
34337     if (this.editor) {
34338         this.editor.tree = this;
34339         this.editor = Roo.factory(this.editor, Roo.tree);
34340     }
34341     
34342     if (this.selModel) {
34343         this.selModel = Roo.factory(this.selModel, Roo.tree);
34344     }
34345    
34346 };
34347 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34348     rootVisible : true,
34349     animate: Roo.enableFx,
34350     lines : true,
34351     enableDD : false,
34352     hlDrop : Roo.enableFx,
34353   
34354     renderer: false,
34355     
34356     rendererTip: false,
34357     // private
34358     restrictExpand : function(node){
34359         var p = node.parentNode;
34360         if(p){
34361             if(p.expandedChild && p.expandedChild.parentNode == p){
34362                 p.expandedChild.collapse();
34363             }
34364             p.expandedChild = node;
34365         }
34366     },
34367
34368     // private override
34369     setRootNode : function(node){
34370         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34371         if(!this.rootVisible){
34372             node.ui = new Roo.tree.RootTreeNodeUI(node);
34373         }
34374         return node;
34375     },
34376
34377     /**
34378      * Returns the container element for this TreePanel
34379      */
34380     getEl : function(){
34381         return this.el;
34382     },
34383
34384     /**
34385      * Returns the default TreeLoader for this TreePanel
34386      */
34387     getLoader : function(){
34388         return this.loader;
34389     },
34390
34391     /**
34392      * Expand all nodes
34393      */
34394     expandAll : function(){
34395         this.root.expand(true);
34396     },
34397
34398     /**
34399      * Collapse all nodes
34400      */
34401     collapseAll : function(){
34402         this.root.collapse(true);
34403     },
34404
34405     /**
34406      * Returns the selection model used by this TreePanel
34407      */
34408     getSelectionModel : function(){
34409         if(!this.selModel){
34410             this.selModel = new Roo.tree.DefaultSelectionModel();
34411         }
34412         return this.selModel;
34413     },
34414
34415     /**
34416      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34417      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34418      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34419      * @return {Array}
34420      */
34421     getChecked : function(a, startNode){
34422         startNode = startNode || this.root;
34423         var r = [];
34424         var f = function(){
34425             if(this.attributes.checked){
34426                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34427             }
34428         }
34429         startNode.cascade(f);
34430         return r;
34431     },
34432
34433     /**
34434      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34435      * @param {String} path
34436      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34437      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34438      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34439      */
34440     expandPath : function(path, attr, callback){
34441         attr = attr || "id";
34442         var keys = path.split(this.pathSeparator);
34443         var curNode = this.root;
34444         if(curNode.attributes[attr] != keys[1]){ // invalid root
34445             if(callback){
34446                 callback(false, null);
34447             }
34448             return;
34449         }
34450         var index = 1;
34451         var f = function(){
34452             if(++index == keys.length){
34453                 if(callback){
34454                     callback(true, curNode);
34455                 }
34456                 return;
34457             }
34458             var c = curNode.findChild(attr, keys[index]);
34459             if(!c){
34460                 if(callback){
34461                     callback(false, curNode);
34462                 }
34463                 return;
34464             }
34465             curNode = c;
34466             c.expand(false, false, f);
34467         };
34468         curNode.expand(false, false, f);
34469     },
34470
34471     /**
34472      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34473      * @param {String} path
34474      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34475      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34476      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34477      */
34478     selectPath : function(path, attr, callback){
34479         attr = attr || "id";
34480         var keys = path.split(this.pathSeparator);
34481         var v = keys.pop();
34482         if(keys.length > 0){
34483             var f = function(success, node){
34484                 if(success && node){
34485                     var n = node.findChild(attr, v);
34486                     if(n){
34487                         n.select();
34488                         if(callback){
34489                             callback(true, n);
34490                         }
34491                     }else if(callback){
34492                         callback(false, n);
34493                     }
34494                 }else{
34495                     if(callback){
34496                         callback(false, n);
34497                     }
34498                 }
34499             };
34500             this.expandPath(keys.join(this.pathSeparator), attr, f);
34501         }else{
34502             this.root.select();
34503             if(callback){
34504                 callback(true, this.root);
34505             }
34506         }
34507     },
34508
34509     getTreeEl : function(){
34510         return this.el;
34511     },
34512
34513     /**
34514      * Trigger rendering of this TreePanel
34515      */
34516     render : function(){
34517         if (this.innerCt) {
34518             return this; // stop it rendering more than once!!
34519         }
34520         
34521         this.innerCt = this.el.createChild({tag:"ul",
34522                cls:"x-tree-root-ct " +
34523                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34524
34525         if(this.containerScroll){
34526             Roo.dd.ScrollManager.register(this.el);
34527         }
34528         if((this.enableDD || this.enableDrop) && !this.dropZone){
34529            /**
34530             * The dropZone used by this tree if drop is enabled
34531             * @type Roo.tree.TreeDropZone
34532             */
34533              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34534                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34535            });
34536         }
34537         if((this.enableDD || this.enableDrag) && !this.dragZone){
34538            /**
34539             * The dragZone used by this tree if drag is enabled
34540             * @type Roo.tree.TreeDragZone
34541             */
34542             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34543                ddGroup: this.ddGroup || "TreeDD",
34544                scroll: this.ddScroll
34545            });
34546         }
34547         this.getSelectionModel().init(this);
34548         if (!this.root) {
34549             Roo.log("ROOT not set in tree");
34550             return this;
34551         }
34552         this.root.render();
34553         if(!this.rootVisible){
34554             this.root.renderChildren();
34555         }
34556         return this;
34557     }
34558 });/*
34559  * Based on:
34560  * Ext JS Library 1.1.1
34561  * Copyright(c) 2006-2007, Ext JS, LLC.
34562  *
34563  * Originally Released Under LGPL - original licence link has changed is not relivant.
34564  *
34565  * Fork - LGPL
34566  * <script type="text/javascript">
34567  */
34568  
34569
34570 /**
34571  * @class Roo.tree.DefaultSelectionModel
34572  * @extends Roo.util.Observable
34573  * The default single selection for a TreePanel.
34574  * @param {Object} cfg Configuration
34575  */
34576 Roo.tree.DefaultSelectionModel = function(cfg){
34577    this.selNode = null;
34578    
34579    
34580    
34581    this.addEvents({
34582        /**
34583         * @event selectionchange
34584         * Fires when the selected node changes
34585         * @param {DefaultSelectionModel} this
34586         * @param {TreeNode} node the new selection
34587         */
34588        "selectionchange" : true,
34589
34590        /**
34591         * @event beforeselect
34592         * Fires before the selected node changes, return false to cancel the change
34593         * @param {DefaultSelectionModel} this
34594         * @param {TreeNode} node the new selection
34595         * @param {TreeNode} node the old selection
34596         */
34597        "beforeselect" : true
34598    });
34599    
34600     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34601 };
34602
34603 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34604     init : function(tree){
34605         this.tree = tree;
34606         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34607         tree.on("click", this.onNodeClick, this);
34608     },
34609     
34610     onNodeClick : function(node, e){
34611         if (e.ctrlKey && this.selNode == node)  {
34612             this.unselect(node);
34613             return;
34614         }
34615         this.select(node);
34616     },
34617     
34618     /**
34619      * Select a node.
34620      * @param {TreeNode} node The node to select
34621      * @return {TreeNode} The selected node
34622      */
34623     select : function(node){
34624         var last = this.selNode;
34625         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34626             if(last){
34627                 last.ui.onSelectedChange(false);
34628             }
34629             this.selNode = node;
34630             node.ui.onSelectedChange(true);
34631             this.fireEvent("selectionchange", this, node, last);
34632         }
34633         return node;
34634     },
34635     
34636     /**
34637      * Deselect a node.
34638      * @param {TreeNode} node The node to unselect
34639      */
34640     unselect : function(node){
34641         if(this.selNode == node){
34642             this.clearSelections();
34643         }    
34644     },
34645     
34646     /**
34647      * Clear all selections
34648      */
34649     clearSelections : function(){
34650         var n = this.selNode;
34651         if(n){
34652             n.ui.onSelectedChange(false);
34653             this.selNode = null;
34654             this.fireEvent("selectionchange", this, null);
34655         }
34656         return n;
34657     },
34658     
34659     /**
34660      * Get the selected node
34661      * @return {TreeNode} The selected node
34662      */
34663     getSelectedNode : function(){
34664         return this.selNode;    
34665     },
34666     
34667     /**
34668      * Returns true if the node is selected
34669      * @param {TreeNode} node The node to check
34670      * @return {Boolean}
34671      */
34672     isSelected : function(node){
34673         return this.selNode == node;  
34674     },
34675
34676     /**
34677      * Selects the node above the selected node in the tree, intelligently walking the nodes
34678      * @return TreeNode The new selection
34679      */
34680     selectPrevious : function(){
34681         var s = this.selNode || this.lastSelNode;
34682         if(!s){
34683             return null;
34684         }
34685         var ps = s.previousSibling;
34686         if(ps){
34687             if(!ps.isExpanded() || ps.childNodes.length < 1){
34688                 return this.select(ps);
34689             } else{
34690                 var lc = ps.lastChild;
34691                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34692                     lc = lc.lastChild;
34693                 }
34694                 return this.select(lc);
34695             }
34696         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34697             return this.select(s.parentNode);
34698         }
34699         return null;
34700     },
34701
34702     /**
34703      * Selects the node above the selected node in the tree, intelligently walking the nodes
34704      * @return TreeNode The new selection
34705      */
34706     selectNext : function(){
34707         var s = this.selNode || this.lastSelNode;
34708         if(!s){
34709             return null;
34710         }
34711         if(s.firstChild && s.isExpanded()){
34712              return this.select(s.firstChild);
34713          }else if(s.nextSibling){
34714              return this.select(s.nextSibling);
34715          }else if(s.parentNode){
34716             var newS = null;
34717             s.parentNode.bubble(function(){
34718                 if(this.nextSibling){
34719                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34720                     return false;
34721                 }
34722             });
34723             return newS;
34724          }
34725         return null;
34726     },
34727
34728     onKeyDown : function(e){
34729         var s = this.selNode || this.lastSelNode;
34730         // undesirable, but required
34731         var sm = this;
34732         if(!s){
34733             return;
34734         }
34735         var k = e.getKey();
34736         switch(k){
34737              case e.DOWN:
34738                  e.stopEvent();
34739                  this.selectNext();
34740              break;
34741              case e.UP:
34742                  e.stopEvent();
34743                  this.selectPrevious();
34744              break;
34745              case e.RIGHT:
34746                  e.preventDefault();
34747                  if(s.hasChildNodes()){
34748                      if(!s.isExpanded()){
34749                          s.expand();
34750                      }else if(s.firstChild){
34751                          this.select(s.firstChild, e);
34752                      }
34753                  }
34754              break;
34755              case e.LEFT:
34756                  e.preventDefault();
34757                  if(s.hasChildNodes() && s.isExpanded()){
34758                      s.collapse();
34759                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34760                      this.select(s.parentNode, e);
34761                  }
34762              break;
34763         };
34764     }
34765 });
34766
34767 /**
34768  * @class Roo.tree.MultiSelectionModel
34769  * @extends Roo.util.Observable
34770  * Multi selection for a TreePanel.
34771  * @param {Object} cfg Configuration
34772  */
34773 Roo.tree.MultiSelectionModel = function(){
34774    this.selNodes = [];
34775    this.selMap = {};
34776    this.addEvents({
34777        /**
34778         * @event selectionchange
34779         * Fires when the selected nodes change
34780         * @param {MultiSelectionModel} this
34781         * @param {Array} nodes Array of the selected nodes
34782         */
34783        "selectionchange" : true
34784    });
34785    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34786    
34787 };
34788
34789 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34790     init : function(tree){
34791         this.tree = tree;
34792         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34793         tree.on("click", this.onNodeClick, this);
34794     },
34795     
34796     onNodeClick : function(node, e){
34797         this.select(node, e, e.ctrlKey);
34798     },
34799     
34800     /**
34801      * Select a node.
34802      * @param {TreeNode} node The node to select
34803      * @param {EventObject} e (optional) An event associated with the selection
34804      * @param {Boolean} keepExisting True to retain existing selections
34805      * @return {TreeNode} The selected node
34806      */
34807     select : function(node, e, keepExisting){
34808         if(keepExisting !== true){
34809             this.clearSelections(true);
34810         }
34811         if(this.isSelected(node)){
34812             this.lastSelNode = node;
34813             return node;
34814         }
34815         this.selNodes.push(node);
34816         this.selMap[node.id] = node;
34817         this.lastSelNode = node;
34818         node.ui.onSelectedChange(true);
34819         this.fireEvent("selectionchange", this, this.selNodes);
34820         return node;
34821     },
34822     
34823     /**
34824      * Deselect a node.
34825      * @param {TreeNode} node The node to unselect
34826      */
34827     unselect : function(node){
34828         if(this.selMap[node.id]){
34829             node.ui.onSelectedChange(false);
34830             var sn = this.selNodes;
34831             var index = -1;
34832             if(sn.indexOf){
34833                 index = sn.indexOf(node);
34834             }else{
34835                 for(var i = 0, len = sn.length; i < len; i++){
34836                     if(sn[i] == node){
34837                         index = i;
34838                         break;
34839                     }
34840                 }
34841             }
34842             if(index != -1){
34843                 this.selNodes.splice(index, 1);
34844             }
34845             delete this.selMap[node.id];
34846             this.fireEvent("selectionchange", this, this.selNodes);
34847         }
34848     },
34849     
34850     /**
34851      * Clear all selections
34852      */
34853     clearSelections : function(suppressEvent){
34854         var sn = this.selNodes;
34855         if(sn.length > 0){
34856             for(var i = 0, len = sn.length; i < len; i++){
34857                 sn[i].ui.onSelectedChange(false);
34858             }
34859             this.selNodes = [];
34860             this.selMap = {};
34861             if(suppressEvent !== true){
34862                 this.fireEvent("selectionchange", this, this.selNodes);
34863             }
34864         }
34865     },
34866     
34867     /**
34868      * Returns true if the node is selected
34869      * @param {TreeNode} node The node to check
34870      * @return {Boolean}
34871      */
34872     isSelected : function(node){
34873         return this.selMap[node.id] ? true : false;  
34874     },
34875     
34876     /**
34877      * Returns an array of the selected nodes
34878      * @return {Array}
34879      */
34880     getSelectedNodes : function(){
34881         return this.selNodes;    
34882     },
34883
34884     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34885
34886     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34887
34888     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34889 });/*
34890  * Based on:
34891  * Ext JS Library 1.1.1
34892  * Copyright(c) 2006-2007, Ext JS, LLC.
34893  *
34894  * Originally Released Under LGPL - original licence link has changed is not relivant.
34895  *
34896  * Fork - LGPL
34897  * <script type="text/javascript">
34898  */
34899  
34900 /**
34901  * @class Roo.tree.TreeNode
34902  * @extends Roo.data.Node
34903  * @cfg {String} text The text for this node
34904  * @cfg {Boolean} expanded true to start the node expanded
34905  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34906  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34907  * @cfg {Boolean} disabled true to start the node disabled
34908  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34909  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34910  * @cfg {String} cls A css class to be added to the node
34911  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34912  * @cfg {String} href URL of the link used for the node (defaults to #)
34913  * @cfg {String} hrefTarget target frame for the link
34914  * @cfg {String} qtip An Ext QuickTip for the node
34915  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34916  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34917  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34918  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34919  * (defaults to undefined with no checkbox rendered)
34920  * @constructor
34921  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34922  */
34923 Roo.tree.TreeNode = function(attributes){
34924     attributes = attributes || {};
34925     if(typeof attributes == "string"){
34926         attributes = {text: attributes};
34927     }
34928     this.childrenRendered = false;
34929     this.rendered = false;
34930     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34931     this.expanded = attributes.expanded === true;
34932     this.isTarget = attributes.isTarget !== false;
34933     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34934     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34935
34936     /**
34937      * Read-only. The text for this node. To change it use setText().
34938      * @type String
34939      */
34940     this.text = attributes.text;
34941     /**
34942      * True if this node is disabled.
34943      * @type Boolean
34944      */
34945     this.disabled = attributes.disabled === true;
34946
34947     this.addEvents({
34948         /**
34949         * @event textchange
34950         * Fires when the text for this node is changed
34951         * @param {Node} this This node
34952         * @param {String} text The new text
34953         * @param {String} oldText The old text
34954         */
34955         "textchange" : true,
34956         /**
34957         * @event beforeexpand
34958         * Fires before this node is expanded, return false to cancel.
34959         * @param {Node} this This node
34960         * @param {Boolean} deep
34961         * @param {Boolean} anim
34962         */
34963         "beforeexpand" : true,
34964         /**
34965         * @event beforecollapse
34966         * Fires before this node is collapsed, return false to cancel.
34967         * @param {Node} this This node
34968         * @param {Boolean} deep
34969         * @param {Boolean} anim
34970         */
34971         "beforecollapse" : true,
34972         /**
34973         * @event expand
34974         * Fires when this node is expanded
34975         * @param {Node} this This node
34976         */
34977         "expand" : true,
34978         /**
34979         * @event disabledchange
34980         * Fires when the disabled status of this node changes
34981         * @param {Node} this This node
34982         * @param {Boolean} disabled
34983         */
34984         "disabledchange" : true,
34985         /**
34986         * @event collapse
34987         * Fires when this node is collapsed
34988         * @param {Node} this This node
34989         */
34990         "collapse" : true,
34991         /**
34992         * @event beforeclick
34993         * Fires before click processing. Return false to cancel the default action.
34994         * @param {Node} this This node
34995         * @param {Roo.EventObject} e The event object
34996         */
34997         "beforeclick":true,
34998         /**
34999         * @event checkchange
35000         * Fires when a node with a checkbox's checked property changes
35001         * @param {Node} this This node
35002         * @param {Boolean} checked
35003         */
35004         "checkchange":true,
35005         /**
35006         * @event click
35007         * Fires when this node is clicked
35008         * @param {Node} this This node
35009         * @param {Roo.EventObject} e The event object
35010         */
35011         "click":true,
35012         /**
35013         * @event dblclick
35014         * Fires when this node is double clicked
35015         * @param {Node} this This node
35016         * @param {Roo.EventObject} e The event object
35017         */
35018         "dblclick":true,
35019         /**
35020         * @event contextmenu
35021         * Fires when this node is right clicked
35022         * @param {Node} this This node
35023         * @param {Roo.EventObject} e The event object
35024         */
35025         "contextmenu":true,
35026         /**
35027         * @event beforechildrenrendered
35028         * Fires right before the child nodes for this node are rendered
35029         * @param {Node} this This node
35030         */
35031         "beforechildrenrendered":true
35032     });
35033
35034     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35035
35036     /**
35037      * Read-only. The UI for this node
35038      * @type TreeNodeUI
35039      */
35040     this.ui = new uiClass(this);
35041     
35042     // finally support items[]
35043     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35044         return;
35045     }
35046     
35047     
35048     Roo.each(this.attributes.items, function(c) {
35049         this.appendChild(Roo.factory(c,Roo.Tree));
35050     }, this);
35051     delete this.attributes.items;
35052     
35053     
35054     
35055 };
35056 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35057     preventHScroll: true,
35058     /**
35059      * Returns true if this node is expanded
35060      * @return {Boolean}
35061      */
35062     isExpanded : function(){
35063         return this.expanded;
35064     },
35065
35066     /**
35067      * Returns the UI object for this node
35068      * @return {TreeNodeUI}
35069      */
35070     getUI : function(){
35071         return this.ui;
35072     },
35073
35074     // private override
35075     setFirstChild : function(node){
35076         var of = this.firstChild;
35077         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35078         if(this.childrenRendered && of && node != of){
35079             of.renderIndent(true, true);
35080         }
35081         if(this.rendered){
35082             this.renderIndent(true, true);
35083         }
35084     },
35085
35086     // private override
35087     setLastChild : function(node){
35088         var ol = this.lastChild;
35089         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35090         if(this.childrenRendered && ol && node != ol){
35091             ol.renderIndent(true, true);
35092         }
35093         if(this.rendered){
35094             this.renderIndent(true, true);
35095         }
35096     },
35097
35098     // these methods are overridden to provide lazy rendering support
35099     // private override
35100     appendChild : function()
35101     {
35102         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35103         if(node && this.childrenRendered){
35104             node.render();
35105         }
35106         this.ui.updateExpandIcon();
35107         return node;
35108     },
35109
35110     // private override
35111     removeChild : function(node){
35112         this.ownerTree.getSelectionModel().unselect(node);
35113         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35114         // if it's been rendered remove dom node
35115         if(this.childrenRendered){
35116             node.ui.remove();
35117         }
35118         if(this.childNodes.length < 1){
35119             this.collapse(false, false);
35120         }else{
35121             this.ui.updateExpandIcon();
35122         }
35123         if(!this.firstChild) {
35124             this.childrenRendered = false;
35125         }
35126         return node;
35127     },
35128
35129     // private override
35130     insertBefore : function(node, refNode){
35131         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35132         if(newNode && refNode && this.childrenRendered){
35133             node.render();
35134         }
35135         this.ui.updateExpandIcon();
35136         return newNode;
35137     },
35138
35139     /**
35140      * Sets the text for this node
35141      * @param {String} text
35142      */
35143     setText : function(text){
35144         var oldText = this.text;
35145         this.text = text;
35146         this.attributes.text = text;
35147         if(this.rendered){ // event without subscribing
35148             this.ui.onTextChange(this, text, oldText);
35149         }
35150         this.fireEvent("textchange", this, text, oldText);
35151     },
35152
35153     /**
35154      * Triggers selection of this node
35155      */
35156     select : function(){
35157         this.getOwnerTree().getSelectionModel().select(this);
35158     },
35159
35160     /**
35161      * Triggers deselection of this node
35162      */
35163     unselect : function(){
35164         this.getOwnerTree().getSelectionModel().unselect(this);
35165     },
35166
35167     /**
35168      * Returns true if this node is selected
35169      * @return {Boolean}
35170      */
35171     isSelected : function(){
35172         return this.getOwnerTree().getSelectionModel().isSelected(this);
35173     },
35174
35175     /**
35176      * Expand this node.
35177      * @param {Boolean} deep (optional) True to expand all children as well
35178      * @param {Boolean} anim (optional) false to cancel the default animation
35179      * @param {Function} callback (optional) A callback to be called when
35180      * expanding this node completes (does not wait for deep expand to complete).
35181      * Called with 1 parameter, this node.
35182      */
35183     expand : function(deep, anim, callback){
35184         if(!this.expanded){
35185             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35186                 return;
35187             }
35188             if(!this.childrenRendered){
35189                 this.renderChildren();
35190             }
35191             this.expanded = true;
35192             
35193             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35194                 this.ui.animExpand(function(){
35195                     this.fireEvent("expand", this);
35196                     if(typeof callback == "function"){
35197                         callback(this);
35198                     }
35199                     if(deep === true){
35200                         this.expandChildNodes(true);
35201                     }
35202                 }.createDelegate(this));
35203                 return;
35204             }else{
35205                 this.ui.expand();
35206                 this.fireEvent("expand", this);
35207                 if(typeof callback == "function"){
35208                     callback(this);
35209                 }
35210             }
35211         }else{
35212            if(typeof callback == "function"){
35213                callback(this);
35214            }
35215         }
35216         if(deep === true){
35217             this.expandChildNodes(true);
35218         }
35219     },
35220
35221     isHiddenRoot : function(){
35222         return this.isRoot && !this.getOwnerTree().rootVisible;
35223     },
35224
35225     /**
35226      * Collapse this node.
35227      * @param {Boolean} deep (optional) True to collapse all children as well
35228      * @param {Boolean} anim (optional) false to cancel the default animation
35229      */
35230     collapse : function(deep, anim){
35231         if(this.expanded && !this.isHiddenRoot()){
35232             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35233                 return;
35234             }
35235             this.expanded = false;
35236             if((this.getOwnerTree().animate && anim !== false) || anim){
35237                 this.ui.animCollapse(function(){
35238                     this.fireEvent("collapse", this);
35239                     if(deep === true){
35240                         this.collapseChildNodes(true);
35241                     }
35242                 }.createDelegate(this));
35243                 return;
35244             }else{
35245                 this.ui.collapse();
35246                 this.fireEvent("collapse", this);
35247             }
35248         }
35249         if(deep === true){
35250             var cs = this.childNodes;
35251             for(var i = 0, len = cs.length; i < len; i++) {
35252                 cs[i].collapse(true, false);
35253             }
35254         }
35255     },
35256
35257     // private
35258     delayedExpand : function(delay){
35259         if(!this.expandProcId){
35260             this.expandProcId = this.expand.defer(delay, this);
35261         }
35262     },
35263
35264     // private
35265     cancelExpand : function(){
35266         if(this.expandProcId){
35267             clearTimeout(this.expandProcId);
35268         }
35269         this.expandProcId = false;
35270     },
35271
35272     /**
35273      * Toggles expanded/collapsed state of the node
35274      */
35275     toggle : function(){
35276         if(this.expanded){
35277             this.collapse();
35278         }else{
35279             this.expand();
35280         }
35281     },
35282
35283     /**
35284      * Ensures all parent nodes are expanded
35285      */
35286     ensureVisible : function(callback){
35287         var tree = this.getOwnerTree();
35288         tree.expandPath(this.parentNode.getPath(), false, function(){
35289             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35290             Roo.callback(callback);
35291         }.createDelegate(this));
35292     },
35293
35294     /**
35295      * Expand all child nodes
35296      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35297      */
35298     expandChildNodes : function(deep){
35299         var cs = this.childNodes;
35300         for(var i = 0, len = cs.length; i < len; i++) {
35301                 cs[i].expand(deep);
35302         }
35303     },
35304
35305     /**
35306      * Collapse all child nodes
35307      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35308      */
35309     collapseChildNodes : function(deep){
35310         var cs = this.childNodes;
35311         for(var i = 0, len = cs.length; i < len; i++) {
35312                 cs[i].collapse(deep);
35313         }
35314     },
35315
35316     /**
35317      * Disables this node
35318      */
35319     disable : function(){
35320         this.disabled = true;
35321         this.unselect();
35322         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35323             this.ui.onDisableChange(this, true);
35324         }
35325         this.fireEvent("disabledchange", this, true);
35326     },
35327
35328     /**
35329      * Enables this node
35330      */
35331     enable : function(){
35332         this.disabled = false;
35333         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35334             this.ui.onDisableChange(this, false);
35335         }
35336         this.fireEvent("disabledchange", this, false);
35337     },
35338
35339     // private
35340     renderChildren : function(suppressEvent){
35341         if(suppressEvent !== false){
35342             this.fireEvent("beforechildrenrendered", this);
35343         }
35344         var cs = this.childNodes;
35345         for(var i = 0, len = cs.length; i < len; i++){
35346             cs[i].render(true);
35347         }
35348         this.childrenRendered = true;
35349     },
35350
35351     // private
35352     sort : function(fn, scope){
35353         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35354         if(this.childrenRendered){
35355             var cs = this.childNodes;
35356             for(var i = 0, len = cs.length; i < len; i++){
35357                 cs[i].render(true);
35358             }
35359         }
35360     },
35361
35362     // private
35363     render : function(bulkRender){
35364         this.ui.render(bulkRender);
35365         if(!this.rendered){
35366             this.rendered = true;
35367             if(this.expanded){
35368                 this.expanded = false;
35369                 this.expand(false, false);
35370             }
35371         }
35372     },
35373
35374     // private
35375     renderIndent : function(deep, refresh){
35376         if(refresh){
35377             this.ui.childIndent = null;
35378         }
35379         this.ui.renderIndent();
35380         if(deep === true && this.childrenRendered){
35381             var cs = this.childNodes;
35382             for(var i = 0, len = cs.length; i < len; i++){
35383                 cs[i].renderIndent(true, refresh);
35384             }
35385         }
35386     }
35387 });/*
35388  * Based on:
35389  * Ext JS Library 1.1.1
35390  * Copyright(c) 2006-2007, Ext JS, LLC.
35391  *
35392  * Originally Released Under LGPL - original licence link has changed is not relivant.
35393  *
35394  * Fork - LGPL
35395  * <script type="text/javascript">
35396  */
35397  
35398 /**
35399  * @class Roo.tree.AsyncTreeNode
35400  * @extends Roo.tree.TreeNode
35401  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35402  * @constructor
35403  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35404  */
35405  Roo.tree.AsyncTreeNode = function(config){
35406     this.loaded = false;
35407     this.loading = false;
35408     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35409     /**
35410     * @event beforeload
35411     * Fires before this node is loaded, return false to cancel
35412     * @param {Node} this This node
35413     */
35414     this.addEvents({'beforeload':true, 'load': true});
35415     /**
35416     * @event load
35417     * Fires when this node is loaded
35418     * @param {Node} this This node
35419     */
35420     /**
35421      * The loader used by this node (defaults to using the tree's defined loader)
35422      * @type TreeLoader
35423      * @property loader
35424      */
35425 };
35426 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35427     expand : function(deep, anim, callback){
35428         if(this.loading){ // if an async load is already running, waiting til it's done
35429             var timer;
35430             var f = function(){
35431                 if(!this.loading){ // done loading
35432                     clearInterval(timer);
35433                     this.expand(deep, anim, callback);
35434                 }
35435             }.createDelegate(this);
35436             timer = setInterval(f, 200);
35437             return;
35438         }
35439         if(!this.loaded){
35440             if(this.fireEvent("beforeload", this) === false){
35441                 return;
35442             }
35443             this.loading = true;
35444             this.ui.beforeLoad(this);
35445             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35446             if(loader){
35447                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35448                 return;
35449             }
35450         }
35451         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35452     },
35453     
35454     /**
35455      * Returns true if this node is currently loading
35456      * @return {Boolean}
35457      */
35458     isLoading : function(){
35459         return this.loading;  
35460     },
35461     
35462     loadComplete : function(deep, anim, callback){
35463         this.loading = false;
35464         this.loaded = true;
35465         this.ui.afterLoad(this);
35466         this.fireEvent("load", this);
35467         this.expand(deep, anim, callback);
35468     },
35469     
35470     /**
35471      * Returns true if this node has been loaded
35472      * @return {Boolean}
35473      */
35474     isLoaded : function(){
35475         return this.loaded;
35476     },
35477     
35478     hasChildNodes : function(){
35479         if(!this.isLeaf() && !this.loaded){
35480             return true;
35481         }else{
35482             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35483         }
35484     },
35485
35486     /**
35487      * Trigger a reload for this node
35488      * @param {Function} callback
35489      */
35490     reload : function(callback){
35491         this.collapse(false, false);
35492         while(this.firstChild){
35493             this.removeChild(this.firstChild);
35494         }
35495         this.childrenRendered = false;
35496         this.loaded = false;
35497         if(this.isHiddenRoot()){
35498             this.expanded = false;
35499         }
35500         this.expand(false, false, callback);
35501     }
35502 });/*
35503  * Based on:
35504  * Ext JS Library 1.1.1
35505  * Copyright(c) 2006-2007, Ext JS, LLC.
35506  *
35507  * Originally Released Under LGPL - original licence link has changed is not relivant.
35508  *
35509  * Fork - LGPL
35510  * <script type="text/javascript">
35511  */
35512  
35513 /**
35514  * @class Roo.tree.TreeNodeUI
35515  * @constructor
35516  * @param {Object} node The node to render
35517  * The TreeNode UI implementation is separate from the
35518  * tree implementation. Unless you are customizing the tree UI,
35519  * you should never have to use this directly.
35520  */
35521 Roo.tree.TreeNodeUI = function(node){
35522     this.node = node;
35523     this.rendered = false;
35524     this.animating = false;
35525     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35526 };
35527
35528 Roo.tree.TreeNodeUI.prototype = {
35529     removeChild : function(node){
35530         if(this.rendered){
35531             this.ctNode.removeChild(node.ui.getEl());
35532         }
35533     },
35534
35535     beforeLoad : function(){
35536          this.addClass("x-tree-node-loading");
35537     },
35538
35539     afterLoad : function(){
35540          this.removeClass("x-tree-node-loading");
35541     },
35542
35543     onTextChange : function(node, text, oldText){
35544         if(this.rendered){
35545             this.textNode.innerHTML = text;
35546         }
35547     },
35548
35549     onDisableChange : function(node, state){
35550         this.disabled = state;
35551         if(state){
35552             this.addClass("x-tree-node-disabled");
35553         }else{
35554             this.removeClass("x-tree-node-disabled");
35555         }
35556     },
35557
35558     onSelectedChange : function(state){
35559         if(state){
35560             this.focus();
35561             this.addClass("x-tree-selected");
35562         }else{
35563             //this.blur();
35564             this.removeClass("x-tree-selected");
35565         }
35566     },
35567
35568     onMove : function(tree, node, oldParent, newParent, index, refNode){
35569         this.childIndent = null;
35570         if(this.rendered){
35571             var targetNode = newParent.ui.getContainer();
35572             if(!targetNode){//target not rendered
35573                 this.holder = document.createElement("div");
35574                 this.holder.appendChild(this.wrap);
35575                 return;
35576             }
35577             var insertBefore = refNode ? refNode.ui.getEl() : null;
35578             if(insertBefore){
35579                 targetNode.insertBefore(this.wrap, insertBefore);
35580             }else{
35581                 targetNode.appendChild(this.wrap);
35582             }
35583             this.node.renderIndent(true);
35584         }
35585     },
35586
35587     addClass : function(cls){
35588         if(this.elNode){
35589             Roo.fly(this.elNode).addClass(cls);
35590         }
35591     },
35592
35593     removeClass : function(cls){
35594         if(this.elNode){
35595             Roo.fly(this.elNode).removeClass(cls);
35596         }
35597     },
35598
35599     remove : function(){
35600         if(this.rendered){
35601             this.holder = document.createElement("div");
35602             this.holder.appendChild(this.wrap);
35603         }
35604     },
35605
35606     fireEvent : function(){
35607         return this.node.fireEvent.apply(this.node, arguments);
35608     },
35609
35610     initEvents : function(){
35611         this.node.on("move", this.onMove, this);
35612         var E = Roo.EventManager;
35613         var a = this.anchor;
35614
35615         var el = Roo.fly(a, '_treeui');
35616
35617         if(Roo.isOpera){ // opera render bug ignores the CSS
35618             el.setStyle("text-decoration", "none");
35619         }
35620
35621         el.on("click", this.onClick, this);
35622         el.on("dblclick", this.onDblClick, this);
35623
35624         if(this.checkbox){
35625             Roo.EventManager.on(this.checkbox,
35626                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35627         }
35628
35629         el.on("contextmenu", this.onContextMenu, this);
35630
35631         var icon = Roo.fly(this.iconNode);
35632         icon.on("click", this.onClick, this);
35633         icon.on("dblclick", this.onDblClick, this);
35634         icon.on("contextmenu", this.onContextMenu, this);
35635         E.on(this.ecNode, "click", this.ecClick, this, true);
35636
35637         if(this.node.disabled){
35638             this.addClass("x-tree-node-disabled");
35639         }
35640         if(this.node.hidden){
35641             this.addClass("x-tree-node-disabled");
35642         }
35643         var ot = this.node.getOwnerTree();
35644         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35645         if(dd && (!this.node.isRoot || ot.rootVisible)){
35646             Roo.dd.Registry.register(this.elNode, {
35647                 node: this.node,
35648                 handles: this.getDDHandles(),
35649                 isHandle: false
35650             });
35651         }
35652     },
35653
35654     getDDHandles : function(){
35655         return [this.iconNode, this.textNode];
35656     },
35657
35658     hide : function(){
35659         if(this.rendered){
35660             this.wrap.style.display = "none";
35661         }
35662     },
35663
35664     show : function(){
35665         if(this.rendered){
35666             this.wrap.style.display = "";
35667         }
35668     },
35669
35670     onContextMenu : function(e){
35671         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35672             e.preventDefault();
35673             this.focus();
35674             this.fireEvent("contextmenu", this.node, e);
35675         }
35676     },
35677
35678     onClick : function(e){
35679         if(this.dropping){
35680             e.stopEvent();
35681             return;
35682         }
35683         if(this.fireEvent("beforeclick", this.node, e) !== false){
35684             if(!this.disabled && this.node.attributes.href){
35685                 this.fireEvent("click", this.node, e);
35686                 return;
35687             }
35688             e.preventDefault();
35689             if(this.disabled){
35690                 return;
35691             }
35692
35693             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35694                 this.node.toggle();
35695             }
35696
35697             this.fireEvent("click", this.node, e);
35698         }else{
35699             e.stopEvent();
35700         }
35701     },
35702
35703     onDblClick : function(e){
35704         e.preventDefault();
35705         if(this.disabled){
35706             return;
35707         }
35708         if(this.checkbox){
35709             this.toggleCheck();
35710         }
35711         if(!this.animating && this.node.hasChildNodes()){
35712             this.node.toggle();
35713         }
35714         this.fireEvent("dblclick", this.node, e);
35715     },
35716
35717     onCheckChange : function(){
35718         var checked = this.checkbox.checked;
35719         this.node.attributes.checked = checked;
35720         this.fireEvent('checkchange', this.node, checked);
35721     },
35722
35723     ecClick : function(e){
35724         if(!this.animating && this.node.hasChildNodes()){
35725             this.node.toggle();
35726         }
35727     },
35728
35729     startDrop : function(){
35730         this.dropping = true;
35731     },
35732
35733     // delayed drop so the click event doesn't get fired on a drop
35734     endDrop : function(){
35735        setTimeout(function(){
35736            this.dropping = false;
35737        }.createDelegate(this), 50);
35738     },
35739
35740     expand : function(){
35741         this.updateExpandIcon();
35742         this.ctNode.style.display = "";
35743     },
35744
35745     focus : function(){
35746         if(!this.node.preventHScroll){
35747             try{this.anchor.focus();
35748             }catch(e){}
35749         }else if(!Roo.isIE){
35750             try{
35751                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35752                 var l = noscroll.scrollLeft;
35753                 this.anchor.focus();
35754                 noscroll.scrollLeft = l;
35755             }catch(e){}
35756         }
35757     },
35758
35759     toggleCheck : function(value){
35760         var cb = this.checkbox;
35761         if(cb){
35762             cb.checked = (value === undefined ? !cb.checked : value);
35763         }
35764     },
35765
35766     blur : function(){
35767         try{
35768             this.anchor.blur();
35769         }catch(e){}
35770     },
35771
35772     animExpand : function(callback){
35773         var ct = Roo.get(this.ctNode);
35774         ct.stopFx();
35775         if(!this.node.hasChildNodes()){
35776             this.updateExpandIcon();
35777             this.ctNode.style.display = "";
35778             Roo.callback(callback);
35779             return;
35780         }
35781         this.animating = true;
35782         this.updateExpandIcon();
35783
35784         ct.slideIn('t', {
35785            callback : function(){
35786                this.animating = false;
35787                Roo.callback(callback);
35788             },
35789             scope: this,
35790             duration: this.node.ownerTree.duration || .25
35791         });
35792     },
35793
35794     highlight : function(){
35795         var tree = this.node.getOwnerTree();
35796         Roo.fly(this.wrap).highlight(
35797             tree.hlColor || "C3DAF9",
35798             {endColor: tree.hlBaseColor}
35799         );
35800     },
35801
35802     collapse : function(){
35803         this.updateExpandIcon();
35804         this.ctNode.style.display = "none";
35805     },
35806
35807     animCollapse : function(callback){
35808         var ct = Roo.get(this.ctNode);
35809         ct.enableDisplayMode('block');
35810         ct.stopFx();
35811
35812         this.animating = true;
35813         this.updateExpandIcon();
35814
35815         ct.slideOut('t', {
35816             callback : function(){
35817                this.animating = false;
35818                Roo.callback(callback);
35819             },
35820             scope: this,
35821             duration: this.node.ownerTree.duration || .25
35822         });
35823     },
35824
35825     getContainer : function(){
35826         return this.ctNode;
35827     },
35828
35829     getEl : function(){
35830         return this.wrap;
35831     },
35832
35833     appendDDGhost : function(ghostNode){
35834         ghostNode.appendChild(this.elNode.cloneNode(true));
35835     },
35836
35837     getDDRepairXY : function(){
35838         return Roo.lib.Dom.getXY(this.iconNode);
35839     },
35840
35841     onRender : function(){
35842         this.render();
35843     },
35844
35845     render : function(bulkRender){
35846         var n = this.node, a = n.attributes;
35847         var targetNode = n.parentNode ?
35848               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35849
35850         if(!this.rendered){
35851             this.rendered = true;
35852
35853             this.renderElements(n, a, targetNode, bulkRender);
35854
35855             if(a.qtip){
35856                if(this.textNode.setAttributeNS){
35857                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35858                    if(a.qtipTitle){
35859                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35860                    }
35861                }else{
35862                    this.textNode.setAttribute("ext:qtip", a.qtip);
35863                    if(a.qtipTitle){
35864                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35865                    }
35866                }
35867             }else if(a.qtipCfg){
35868                 a.qtipCfg.target = Roo.id(this.textNode);
35869                 Roo.QuickTips.register(a.qtipCfg);
35870             }
35871             this.initEvents();
35872             if(!this.node.expanded){
35873                 this.updateExpandIcon();
35874             }
35875         }else{
35876             if(bulkRender === true) {
35877                 targetNode.appendChild(this.wrap);
35878             }
35879         }
35880     },
35881
35882     renderElements : function(n, a, targetNode, bulkRender)
35883     {
35884         // add some indent caching, this helps performance when rendering a large tree
35885         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35886         var t = n.getOwnerTree();
35887         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35888         if (typeof(n.attributes.html) != 'undefined') {
35889             txt = n.attributes.html;
35890         }
35891         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35892         var cb = typeof a.checked == 'boolean';
35893         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35894         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35895             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35896             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35897             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35898             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35899             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35900              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35901                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35902             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35903             "</li>"];
35904
35905         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35906             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35907                                 n.nextSibling.ui.getEl(), buf.join(""));
35908         }else{
35909             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35910         }
35911
35912         this.elNode = this.wrap.childNodes[0];
35913         this.ctNode = this.wrap.childNodes[1];
35914         var cs = this.elNode.childNodes;
35915         this.indentNode = cs[0];
35916         this.ecNode = cs[1];
35917         this.iconNode = cs[2];
35918         var index = 3;
35919         if(cb){
35920             this.checkbox = cs[3];
35921             index++;
35922         }
35923         this.anchor = cs[index];
35924         this.textNode = cs[index].firstChild;
35925     },
35926
35927     getAnchor : function(){
35928         return this.anchor;
35929     },
35930
35931     getTextEl : function(){
35932         return this.textNode;
35933     },
35934
35935     getIconEl : function(){
35936         return this.iconNode;
35937     },
35938
35939     isChecked : function(){
35940         return this.checkbox ? this.checkbox.checked : false;
35941     },
35942
35943     updateExpandIcon : function(){
35944         if(this.rendered){
35945             var n = this.node, c1, c2;
35946             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35947             var hasChild = n.hasChildNodes();
35948             if(hasChild){
35949                 if(n.expanded){
35950                     cls += "-minus";
35951                     c1 = "x-tree-node-collapsed";
35952                     c2 = "x-tree-node-expanded";
35953                 }else{
35954                     cls += "-plus";
35955                     c1 = "x-tree-node-expanded";
35956                     c2 = "x-tree-node-collapsed";
35957                 }
35958                 if(this.wasLeaf){
35959                     this.removeClass("x-tree-node-leaf");
35960                     this.wasLeaf = false;
35961                 }
35962                 if(this.c1 != c1 || this.c2 != c2){
35963                     Roo.fly(this.elNode).replaceClass(c1, c2);
35964                     this.c1 = c1; this.c2 = c2;
35965                 }
35966             }else{
35967                 // this changes non-leafs into leafs if they have no children.
35968                 // it's not very rational behaviour..
35969                 
35970                 if(!this.wasLeaf && this.node.leaf){
35971                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35972                     delete this.c1;
35973                     delete this.c2;
35974                     this.wasLeaf = true;
35975                 }
35976             }
35977             var ecc = "x-tree-ec-icon "+cls;
35978             if(this.ecc != ecc){
35979                 this.ecNode.className = ecc;
35980                 this.ecc = ecc;
35981             }
35982         }
35983     },
35984
35985     getChildIndent : function(){
35986         if(!this.childIndent){
35987             var buf = [];
35988             var p = this.node;
35989             while(p){
35990                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35991                     if(!p.isLast()) {
35992                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35993                     } else {
35994                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35995                     }
35996                 }
35997                 p = p.parentNode;
35998             }
35999             this.childIndent = buf.join("");
36000         }
36001         return this.childIndent;
36002     },
36003
36004     renderIndent : function(){
36005         if(this.rendered){
36006             var indent = "";
36007             var p = this.node.parentNode;
36008             if(p){
36009                 indent = p.ui.getChildIndent();
36010             }
36011             if(this.indentMarkup != indent){ // don't rerender if not required
36012                 this.indentNode.innerHTML = indent;
36013                 this.indentMarkup = indent;
36014             }
36015             this.updateExpandIcon();
36016         }
36017     }
36018 };
36019
36020 Roo.tree.RootTreeNodeUI = function(){
36021     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36022 };
36023 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36024     render : function(){
36025         if(!this.rendered){
36026             var targetNode = this.node.ownerTree.innerCt.dom;
36027             this.node.expanded = true;
36028             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36029             this.wrap = this.ctNode = targetNode.firstChild;
36030         }
36031     },
36032     collapse : function(){
36033     },
36034     expand : function(){
36035     }
36036 });/*
36037  * Based on:
36038  * Ext JS Library 1.1.1
36039  * Copyright(c) 2006-2007, Ext JS, LLC.
36040  *
36041  * Originally Released Under LGPL - original licence link has changed is not relivant.
36042  *
36043  * Fork - LGPL
36044  * <script type="text/javascript">
36045  */
36046 /**
36047  * @class Roo.tree.TreeLoader
36048  * @extends Roo.util.Observable
36049  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36050  * nodes from a specified URL. The response must be a javascript Array definition
36051  * who's elements are node definition objects. eg:
36052  * <pre><code>
36053 {  success : true,
36054    data :      [
36055    
36056     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36057     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36058     ]
36059 }
36060
36061
36062 </code></pre>
36063  * <br><br>
36064  * The old style respose with just an array is still supported, but not recommended.
36065  * <br><br>
36066  *
36067  * A server request is sent, and child nodes are loaded only when a node is expanded.
36068  * The loading node's id is passed to the server under the parameter name "node" to
36069  * enable the server to produce the correct child nodes.
36070  * <br><br>
36071  * To pass extra parameters, an event handler may be attached to the "beforeload"
36072  * event, and the parameters specified in the TreeLoader's baseParams property:
36073  * <pre><code>
36074     myTreeLoader.on("beforeload", function(treeLoader, node) {
36075         this.baseParams.category = node.attributes.category;
36076     }, this);
36077     
36078 </code></pre>
36079  *
36080  * This would pass an HTTP parameter called "category" to the server containing
36081  * the value of the Node's "category" attribute.
36082  * @constructor
36083  * Creates a new Treeloader.
36084  * @param {Object} config A config object containing config properties.
36085  */
36086 Roo.tree.TreeLoader = function(config){
36087     this.baseParams = {};
36088     this.requestMethod = "POST";
36089     Roo.apply(this, config);
36090
36091     this.addEvents({
36092     
36093         /**
36094          * @event beforeload
36095          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36096          * @param {Object} This TreeLoader object.
36097          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36098          * @param {Object} callback The callback function specified in the {@link #load} call.
36099          */
36100         beforeload : true,
36101         /**
36102          * @event load
36103          * Fires when the node has been successfuly loaded.
36104          * @param {Object} This TreeLoader object.
36105          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36106          * @param {Object} response The response object containing the data from the server.
36107          */
36108         load : true,
36109         /**
36110          * @event loadexception
36111          * Fires if the network request failed.
36112          * @param {Object} This TreeLoader object.
36113          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36114          * @param {Object} response The response object containing the data from the server.
36115          */
36116         loadexception : true,
36117         /**
36118          * @event create
36119          * Fires before a node is created, enabling you to return custom Node types 
36120          * @param {Object} This TreeLoader object.
36121          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36122          */
36123         create : true
36124     });
36125
36126     Roo.tree.TreeLoader.superclass.constructor.call(this);
36127 };
36128
36129 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36130     /**
36131     * @cfg {String} dataUrl The URL from which to request a Json string which
36132     * specifies an array of node definition object representing the child nodes
36133     * to be loaded.
36134     */
36135     /**
36136     * @cfg {String} requestMethod either GET or POST
36137     * defaults to POST (due to BC)
36138     * to be loaded.
36139     */
36140     /**
36141     * @cfg {Object} baseParams (optional) An object containing properties which
36142     * specify HTTP parameters to be passed to each request for child nodes.
36143     */
36144     /**
36145     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36146     * created by this loader. If the attributes sent by the server have an attribute in this object,
36147     * they take priority.
36148     */
36149     /**
36150     * @cfg {Object} uiProviders (optional) An object containing properties which
36151     * 
36152     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36153     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36154     * <i>uiProvider</i> attribute of a returned child node is a string rather
36155     * than a reference to a TreeNodeUI implementation, this that string value
36156     * is used as a property name in the uiProviders object. You can define the provider named
36157     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36158     */
36159     uiProviders : {},
36160
36161     /**
36162     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36163     * child nodes before loading.
36164     */
36165     clearOnLoad : true,
36166
36167     /**
36168     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36169     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36170     * Grid query { data : [ .....] }
36171     */
36172     
36173     root : false,
36174      /**
36175     * @cfg {String} queryParam (optional) 
36176     * Name of the query as it will be passed on the querystring (defaults to 'node')
36177     * eg. the request will be ?node=[id]
36178     */
36179     
36180     
36181     queryParam: false,
36182     
36183     /**
36184      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36185      * This is called automatically when a node is expanded, but may be used to reload
36186      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36187      * @param {Roo.tree.TreeNode} node
36188      * @param {Function} callback
36189      */
36190     load : function(node, callback){
36191         if(this.clearOnLoad){
36192             while(node.firstChild){
36193                 node.removeChild(node.firstChild);
36194             }
36195         }
36196         if(node.attributes.children){ // preloaded json children
36197             var cs = node.attributes.children;
36198             for(var i = 0, len = cs.length; i < len; i++){
36199                 node.appendChild(this.createNode(cs[i]));
36200             }
36201             if(typeof callback == "function"){
36202                 callback();
36203             }
36204         }else if(this.dataUrl){
36205             this.requestData(node, callback);
36206         }
36207     },
36208
36209     getParams: function(node){
36210         var buf = [], bp = this.baseParams;
36211         for(var key in bp){
36212             if(typeof bp[key] != "function"){
36213                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36214             }
36215         }
36216         var n = this.queryParam === false ? 'node' : this.queryParam;
36217         buf.push(n + "=", encodeURIComponent(node.id));
36218         return buf.join("");
36219     },
36220
36221     requestData : function(node, callback){
36222         if(this.fireEvent("beforeload", this, node, callback) !== false){
36223             this.transId = Roo.Ajax.request({
36224                 method:this.requestMethod,
36225                 url: this.dataUrl||this.url,
36226                 success: this.handleResponse,
36227                 failure: this.handleFailure,
36228                 scope: this,
36229                 argument: {callback: callback, node: node},
36230                 params: this.getParams(node)
36231             });
36232         }else{
36233             // if the load is cancelled, make sure we notify
36234             // the node that we are done
36235             if(typeof callback == "function"){
36236                 callback();
36237             }
36238         }
36239     },
36240
36241     isLoading : function(){
36242         return this.transId ? true : false;
36243     },
36244
36245     abort : function(){
36246         if(this.isLoading()){
36247             Roo.Ajax.abort(this.transId);
36248         }
36249     },
36250
36251     // private
36252     createNode : function(attr)
36253     {
36254         // apply baseAttrs, nice idea Corey!
36255         if(this.baseAttrs){
36256             Roo.applyIf(attr, this.baseAttrs);
36257         }
36258         if(this.applyLoader !== false){
36259             attr.loader = this;
36260         }
36261         // uiProvider = depreciated..
36262         
36263         if(typeof(attr.uiProvider) == 'string'){
36264            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36265                 /**  eval:var:attr */ eval(attr.uiProvider);
36266         }
36267         if(typeof(this.uiProviders['default']) != 'undefined') {
36268             attr.uiProvider = this.uiProviders['default'];
36269         }
36270         
36271         this.fireEvent('create', this, attr);
36272         
36273         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36274         return(attr.leaf ?
36275                         new Roo.tree.TreeNode(attr) :
36276                         new Roo.tree.AsyncTreeNode(attr));
36277     },
36278
36279     processResponse : function(response, node, callback)
36280     {
36281         var json = response.responseText;
36282         try {
36283             
36284             var o = Roo.decode(json);
36285             
36286             if (this.root === false && typeof(o.success) != undefined) {
36287                 this.root = 'data'; // the default behaviour for list like data..
36288                 }
36289                 
36290             if (this.root !== false &&  !o.success) {
36291                 // it's a failure condition.
36292                 var a = response.argument;
36293                 this.fireEvent("loadexception", this, a.node, response);
36294                 Roo.log("Load failed - should have a handler really");
36295                 return;
36296             }
36297             
36298             
36299             
36300             if (this.root !== false) {
36301                  o = o[this.root];
36302             }
36303             
36304             for(var i = 0, len = o.length; i < len; i++){
36305                 var n = this.createNode(o[i]);
36306                 if(n){
36307                     node.appendChild(n);
36308                 }
36309             }
36310             if(typeof callback == "function"){
36311                 callback(this, node);
36312             }
36313         }catch(e){
36314             this.handleFailure(response);
36315         }
36316     },
36317
36318     handleResponse : function(response){
36319         this.transId = false;
36320         var a = response.argument;
36321         this.processResponse(response, a.node, a.callback);
36322         this.fireEvent("load", this, a.node, response);
36323     },
36324
36325     handleFailure : function(response)
36326     {
36327         // should handle failure better..
36328         this.transId = false;
36329         var a = response.argument;
36330         this.fireEvent("loadexception", this, a.node, response);
36331         if(typeof a.callback == "function"){
36332             a.callback(this, a.node);
36333         }
36334     }
36335 });/*
36336  * Based on:
36337  * Ext JS Library 1.1.1
36338  * Copyright(c) 2006-2007, Ext JS, LLC.
36339  *
36340  * Originally Released Under LGPL - original licence link has changed is not relivant.
36341  *
36342  * Fork - LGPL
36343  * <script type="text/javascript">
36344  */
36345
36346 /**
36347 * @class Roo.tree.TreeFilter
36348 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36349 * @param {TreePanel} tree
36350 * @param {Object} config (optional)
36351  */
36352 Roo.tree.TreeFilter = function(tree, config){
36353     this.tree = tree;
36354     this.filtered = {};
36355     Roo.apply(this, config);
36356 };
36357
36358 Roo.tree.TreeFilter.prototype = {
36359     clearBlank:false,
36360     reverse:false,
36361     autoClear:false,
36362     remove:false,
36363
36364      /**
36365      * Filter the data by a specific attribute.
36366      * @param {String/RegExp} value Either string that the attribute value
36367      * should start with or a RegExp to test against the attribute
36368      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36369      * @param {TreeNode} startNode (optional) The node to start the filter at.
36370      */
36371     filter : function(value, attr, startNode){
36372         attr = attr || "text";
36373         var f;
36374         if(typeof value == "string"){
36375             var vlen = value.length;
36376             // auto clear empty filter
36377             if(vlen == 0 && this.clearBlank){
36378                 this.clear();
36379                 return;
36380             }
36381             value = value.toLowerCase();
36382             f = function(n){
36383                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36384             };
36385         }else if(value.exec){ // regex?
36386             f = function(n){
36387                 return value.test(n.attributes[attr]);
36388             };
36389         }else{
36390             throw 'Illegal filter type, must be string or regex';
36391         }
36392         this.filterBy(f, null, startNode);
36393         },
36394
36395     /**
36396      * Filter by a function. The passed function will be called with each
36397      * node in the tree (or from the startNode). If the function returns true, the node is kept
36398      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36399      * @param {Function} fn The filter function
36400      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36401      */
36402     filterBy : function(fn, scope, startNode){
36403         startNode = startNode || this.tree.root;
36404         if(this.autoClear){
36405             this.clear();
36406         }
36407         var af = this.filtered, rv = this.reverse;
36408         var f = function(n){
36409             if(n == startNode){
36410                 return true;
36411             }
36412             if(af[n.id]){
36413                 return false;
36414             }
36415             var m = fn.call(scope || n, n);
36416             if(!m || rv){
36417                 af[n.id] = n;
36418                 n.ui.hide();
36419                 return false;
36420             }
36421             return true;
36422         };
36423         startNode.cascade(f);
36424         if(this.remove){
36425            for(var id in af){
36426                if(typeof id != "function"){
36427                    var n = af[id];
36428                    if(n && n.parentNode){
36429                        n.parentNode.removeChild(n);
36430                    }
36431                }
36432            }
36433         }
36434     },
36435
36436     /**
36437      * Clears the current filter. Note: with the "remove" option
36438      * set a filter cannot be cleared.
36439      */
36440     clear : function(){
36441         var t = this.tree;
36442         var af = this.filtered;
36443         for(var id in af){
36444             if(typeof id != "function"){
36445                 var n = af[id];
36446                 if(n){
36447                     n.ui.show();
36448                 }
36449             }
36450         }
36451         this.filtered = {};
36452     }
36453 };
36454 /*
36455  * Based on:
36456  * Ext JS Library 1.1.1
36457  * Copyright(c) 2006-2007, Ext JS, LLC.
36458  *
36459  * Originally Released Under LGPL - original licence link has changed is not relivant.
36460  *
36461  * Fork - LGPL
36462  * <script type="text/javascript">
36463  */
36464  
36465
36466 /**
36467  * @class Roo.tree.TreeSorter
36468  * Provides sorting of nodes in a TreePanel
36469  * 
36470  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36471  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36472  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36473  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36474  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36475  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36476  * @constructor
36477  * @param {TreePanel} tree
36478  * @param {Object} config
36479  */
36480 Roo.tree.TreeSorter = function(tree, config){
36481     Roo.apply(this, config);
36482     tree.on("beforechildrenrendered", this.doSort, this);
36483     tree.on("append", this.updateSort, this);
36484     tree.on("insert", this.updateSort, this);
36485     
36486     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36487     var p = this.property || "text";
36488     var sortType = this.sortType;
36489     var fs = this.folderSort;
36490     var cs = this.caseSensitive === true;
36491     var leafAttr = this.leafAttr || 'leaf';
36492
36493     this.sortFn = function(n1, n2){
36494         if(fs){
36495             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36496                 return 1;
36497             }
36498             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36499                 return -1;
36500             }
36501         }
36502         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36503         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36504         if(v1 < v2){
36505                         return dsc ? +1 : -1;
36506                 }else if(v1 > v2){
36507                         return dsc ? -1 : +1;
36508         }else{
36509                 return 0;
36510         }
36511     };
36512 };
36513
36514 Roo.tree.TreeSorter.prototype = {
36515     doSort : function(node){
36516         node.sort(this.sortFn);
36517     },
36518     
36519     compareNodes : function(n1, n2){
36520         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36521     },
36522     
36523     updateSort : function(tree, node){
36524         if(node.childrenRendered){
36525             this.doSort.defer(1, this, [node]);
36526         }
36527     }
36528 };/*
36529  * Based on:
36530  * Ext JS Library 1.1.1
36531  * Copyright(c) 2006-2007, Ext JS, LLC.
36532  *
36533  * Originally Released Under LGPL - original licence link has changed is not relivant.
36534  *
36535  * Fork - LGPL
36536  * <script type="text/javascript">
36537  */
36538
36539 if(Roo.dd.DropZone){
36540     
36541 Roo.tree.TreeDropZone = function(tree, config){
36542     this.allowParentInsert = false;
36543     this.allowContainerDrop = false;
36544     this.appendOnly = false;
36545     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36546     this.tree = tree;
36547     this.lastInsertClass = "x-tree-no-status";
36548     this.dragOverData = {};
36549 };
36550
36551 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36552     ddGroup : "TreeDD",
36553     scroll:  true,
36554     
36555     expandDelay : 1000,
36556     
36557     expandNode : function(node){
36558         if(node.hasChildNodes() && !node.isExpanded()){
36559             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36560         }
36561     },
36562     
36563     queueExpand : function(node){
36564         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36565     },
36566     
36567     cancelExpand : function(){
36568         if(this.expandProcId){
36569             clearTimeout(this.expandProcId);
36570             this.expandProcId = false;
36571         }
36572     },
36573     
36574     isValidDropPoint : function(n, pt, dd, e, data){
36575         if(!n || !data){ return false; }
36576         var targetNode = n.node;
36577         var dropNode = data.node;
36578         // default drop rules
36579         if(!(targetNode && targetNode.isTarget && pt)){
36580             return false;
36581         }
36582         if(pt == "append" && targetNode.allowChildren === false){
36583             return false;
36584         }
36585         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36586             return false;
36587         }
36588         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36589             return false;
36590         }
36591         // reuse the object
36592         var overEvent = this.dragOverData;
36593         overEvent.tree = this.tree;
36594         overEvent.target = targetNode;
36595         overEvent.data = data;
36596         overEvent.point = pt;
36597         overEvent.source = dd;
36598         overEvent.rawEvent = e;
36599         overEvent.dropNode = dropNode;
36600         overEvent.cancel = false;  
36601         var result = this.tree.fireEvent("nodedragover", overEvent);
36602         return overEvent.cancel === false && result !== false;
36603     },
36604     
36605     getDropPoint : function(e, n, dd)
36606     {
36607         var tn = n.node;
36608         if(tn.isRoot){
36609             return tn.allowChildren !== false ? "append" : false; // always append for root
36610         }
36611         var dragEl = n.ddel;
36612         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36613         var y = Roo.lib.Event.getPageY(e);
36614         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36615         
36616         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36617         var noAppend = tn.allowChildren === false;
36618         if(this.appendOnly || tn.parentNode.allowChildren === false){
36619             return noAppend ? false : "append";
36620         }
36621         var noBelow = false;
36622         if(!this.allowParentInsert){
36623             noBelow = tn.hasChildNodes() && tn.isExpanded();
36624         }
36625         var q = (b - t) / (noAppend ? 2 : 3);
36626         if(y >= t && y < (t + q)){
36627             return "above";
36628         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36629             return "below";
36630         }else{
36631             return "append";
36632         }
36633     },
36634     
36635     onNodeEnter : function(n, dd, e, data)
36636     {
36637         this.cancelExpand();
36638     },
36639     
36640     onNodeOver : function(n, dd, e, data)
36641     {
36642        
36643         var pt = this.getDropPoint(e, n, dd);
36644         var node = n.node;
36645         
36646         // auto node expand check
36647         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36648             this.queueExpand(node);
36649         }else if(pt != "append"){
36650             this.cancelExpand();
36651         }
36652         
36653         // set the insert point style on the target node
36654         var returnCls = this.dropNotAllowed;
36655         if(this.isValidDropPoint(n, pt, dd, e, data)){
36656            if(pt){
36657                var el = n.ddel;
36658                var cls;
36659                if(pt == "above"){
36660                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36661                    cls = "x-tree-drag-insert-above";
36662                }else if(pt == "below"){
36663                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36664                    cls = "x-tree-drag-insert-below";
36665                }else{
36666                    returnCls = "x-tree-drop-ok-append";
36667                    cls = "x-tree-drag-append";
36668                }
36669                if(this.lastInsertClass != cls){
36670                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36671                    this.lastInsertClass = cls;
36672                }
36673            }
36674        }
36675        return returnCls;
36676     },
36677     
36678     onNodeOut : function(n, dd, e, data){
36679         
36680         this.cancelExpand();
36681         this.removeDropIndicators(n);
36682     },
36683     
36684     onNodeDrop : function(n, dd, e, data){
36685         var point = this.getDropPoint(e, n, dd);
36686         var targetNode = n.node;
36687         targetNode.ui.startDrop();
36688         if(!this.isValidDropPoint(n, point, dd, e, data)){
36689             targetNode.ui.endDrop();
36690             return false;
36691         }
36692         // first try to find the drop node
36693         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36694         var dropEvent = {
36695             tree : this.tree,
36696             target: targetNode,
36697             data: data,
36698             point: point,
36699             source: dd,
36700             rawEvent: e,
36701             dropNode: dropNode,
36702             cancel: !dropNode   
36703         };
36704         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36705         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36706             targetNode.ui.endDrop();
36707             return false;
36708         }
36709         // allow target changing
36710         targetNode = dropEvent.target;
36711         if(point == "append" && !targetNode.isExpanded()){
36712             targetNode.expand(false, null, function(){
36713                 this.completeDrop(dropEvent);
36714             }.createDelegate(this));
36715         }else{
36716             this.completeDrop(dropEvent);
36717         }
36718         return true;
36719     },
36720     
36721     completeDrop : function(de){
36722         var ns = de.dropNode, p = de.point, t = de.target;
36723         if(!(ns instanceof Array)){
36724             ns = [ns];
36725         }
36726         var n;
36727         for(var i = 0, len = ns.length; i < len; i++){
36728             n = ns[i];
36729             if(p == "above"){
36730                 t.parentNode.insertBefore(n, t);
36731             }else if(p == "below"){
36732                 t.parentNode.insertBefore(n, t.nextSibling);
36733             }else{
36734                 t.appendChild(n);
36735             }
36736         }
36737         n.ui.focus();
36738         if(this.tree.hlDrop){
36739             n.ui.highlight();
36740         }
36741         t.ui.endDrop();
36742         this.tree.fireEvent("nodedrop", de);
36743     },
36744     
36745     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36746         if(this.tree.hlDrop){
36747             dropNode.ui.focus();
36748             dropNode.ui.highlight();
36749         }
36750         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36751     },
36752     
36753     getTree : function(){
36754         return this.tree;
36755     },
36756     
36757     removeDropIndicators : function(n){
36758         if(n && n.ddel){
36759             var el = n.ddel;
36760             Roo.fly(el).removeClass([
36761                     "x-tree-drag-insert-above",
36762                     "x-tree-drag-insert-below",
36763                     "x-tree-drag-append"]);
36764             this.lastInsertClass = "_noclass";
36765         }
36766     },
36767     
36768     beforeDragDrop : function(target, e, id){
36769         this.cancelExpand();
36770         return true;
36771     },
36772     
36773     afterRepair : function(data){
36774         if(data && Roo.enableFx){
36775             data.node.ui.highlight();
36776         }
36777         this.hideProxy();
36778     } 
36779     
36780 });
36781
36782 }
36783 /*
36784  * Based on:
36785  * Ext JS Library 1.1.1
36786  * Copyright(c) 2006-2007, Ext JS, LLC.
36787  *
36788  * Originally Released Under LGPL - original licence link has changed is not relivant.
36789  *
36790  * Fork - LGPL
36791  * <script type="text/javascript">
36792  */
36793  
36794
36795 if(Roo.dd.DragZone){
36796 Roo.tree.TreeDragZone = function(tree, config){
36797     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36798     this.tree = tree;
36799 };
36800
36801 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36802     ddGroup : "TreeDD",
36803    
36804     onBeforeDrag : function(data, e){
36805         var n = data.node;
36806         return n && n.draggable && !n.disabled;
36807     },
36808      
36809     
36810     onInitDrag : function(e){
36811         var data = this.dragData;
36812         this.tree.getSelectionModel().select(data.node);
36813         this.proxy.update("");
36814         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36815         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36816     },
36817     
36818     getRepairXY : function(e, data){
36819         return data.node.ui.getDDRepairXY();
36820     },
36821     
36822     onEndDrag : function(data, e){
36823         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36824         
36825         
36826     },
36827     
36828     onValidDrop : function(dd, e, id){
36829         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36830         this.hideProxy();
36831     },
36832     
36833     beforeInvalidDrop : function(e, id){
36834         // this scrolls the original position back into view
36835         var sm = this.tree.getSelectionModel();
36836         sm.clearSelections();
36837         sm.select(this.dragData.node);
36838     }
36839 });
36840 }/*
36841  * Based on:
36842  * Ext JS Library 1.1.1
36843  * Copyright(c) 2006-2007, Ext JS, LLC.
36844  *
36845  * Originally Released Under LGPL - original licence link has changed is not relivant.
36846  *
36847  * Fork - LGPL
36848  * <script type="text/javascript">
36849  */
36850 /**
36851  * @class Roo.tree.TreeEditor
36852  * @extends Roo.Editor
36853  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36854  * as the editor field.
36855  * @constructor
36856  * @param {Object} config (used to be the tree panel.)
36857  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36858  * 
36859  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36860  * @cfg {Roo.form.TextField|Object} field The field configuration
36861  *
36862  * 
36863  */
36864 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36865     var tree = config;
36866     var field;
36867     if (oldconfig) { // old style..
36868         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36869     } else {
36870         // new style..
36871         tree = config.tree;
36872         config.field = config.field  || {};
36873         config.field.xtype = 'TextField';
36874         field = Roo.factory(config.field, Roo.form);
36875     }
36876     config = config || {};
36877     
36878     
36879     this.addEvents({
36880         /**
36881          * @event beforenodeedit
36882          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36883          * false from the handler of this event.
36884          * @param {Editor} this
36885          * @param {Roo.tree.Node} node 
36886          */
36887         "beforenodeedit" : true
36888     });
36889     
36890     //Roo.log(config);
36891     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36892
36893     this.tree = tree;
36894
36895     tree.on('beforeclick', this.beforeNodeClick, this);
36896     tree.getTreeEl().on('mousedown', this.hide, this);
36897     this.on('complete', this.updateNode, this);
36898     this.on('beforestartedit', this.fitToTree, this);
36899     this.on('startedit', this.bindScroll, this, {delay:10});
36900     this.on('specialkey', this.onSpecialKey, this);
36901 };
36902
36903 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36904     /**
36905      * @cfg {String} alignment
36906      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36907      */
36908     alignment: "l-l",
36909     // inherit
36910     autoSize: false,
36911     /**
36912      * @cfg {Boolean} hideEl
36913      * True to hide the bound element while the editor is displayed (defaults to false)
36914      */
36915     hideEl : false,
36916     /**
36917      * @cfg {String} cls
36918      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36919      */
36920     cls: "x-small-editor x-tree-editor",
36921     /**
36922      * @cfg {Boolean} shim
36923      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36924      */
36925     shim:false,
36926     // inherit
36927     shadow:"frame",
36928     /**
36929      * @cfg {Number} maxWidth
36930      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36931      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36932      * scroll and client offsets into account prior to each edit.
36933      */
36934     maxWidth: 250,
36935
36936     editDelay : 350,
36937
36938     // private
36939     fitToTree : function(ed, el){
36940         var td = this.tree.getTreeEl().dom, nd = el.dom;
36941         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36942             td.scrollLeft = nd.offsetLeft;
36943         }
36944         var w = Math.min(
36945                 this.maxWidth,
36946                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36947         this.setSize(w, '');
36948         
36949         return this.fireEvent('beforenodeedit', this, this.editNode);
36950         
36951     },
36952
36953     // private
36954     triggerEdit : function(node){
36955         this.completeEdit();
36956         this.editNode = node;
36957         this.startEdit(node.ui.textNode, node.text);
36958     },
36959
36960     // private
36961     bindScroll : function(){
36962         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36963     },
36964
36965     // private
36966     beforeNodeClick : function(node, e){
36967         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36968         this.lastClick = new Date();
36969         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36970             e.stopEvent();
36971             this.triggerEdit(node);
36972             return false;
36973         }
36974         return true;
36975     },
36976
36977     // private
36978     updateNode : function(ed, value){
36979         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36980         this.editNode.setText(value);
36981     },
36982
36983     // private
36984     onHide : function(){
36985         Roo.tree.TreeEditor.superclass.onHide.call(this);
36986         if(this.editNode){
36987             this.editNode.ui.focus();
36988         }
36989     },
36990
36991     // private
36992     onSpecialKey : function(field, e){
36993         var k = e.getKey();
36994         if(k == e.ESC){
36995             e.stopEvent();
36996             this.cancelEdit();
36997         }else if(k == e.ENTER && !e.hasModifier()){
36998             e.stopEvent();
36999             this.completeEdit();
37000         }
37001     }
37002 });//<Script type="text/javascript">
37003 /*
37004  * Based on:
37005  * Ext JS Library 1.1.1
37006  * Copyright(c) 2006-2007, Ext JS, LLC.
37007  *
37008  * Originally Released Under LGPL - original licence link has changed is not relivant.
37009  *
37010  * Fork - LGPL
37011  * <script type="text/javascript">
37012  */
37013  
37014 /**
37015  * Not documented??? - probably should be...
37016  */
37017
37018 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37019     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37020     
37021     renderElements : function(n, a, targetNode, bulkRender){
37022         //consel.log("renderElements?");
37023         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37024
37025         var t = n.getOwnerTree();
37026         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37027         
37028         var cols = t.columns;
37029         var bw = t.borderWidth;
37030         var c = cols[0];
37031         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37032          var cb = typeof a.checked == "boolean";
37033         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37034         var colcls = 'x-t-' + tid + '-c0';
37035         var buf = [
37036             '<li class="x-tree-node">',
37037             
37038                 
37039                 '<div class="x-tree-node-el ', a.cls,'">',
37040                     // extran...
37041                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37042                 
37043                 
37044                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37045                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37046                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37047                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37048                            (a.iconCls ? ' '+a.iconCls : ''),
37049                            '" unselectable="on" />',
37050                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37051                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37052                              
37053                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37054                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37055                             '<span unselectable="on" qtip="' + tx + '">',
37056                              tx,
37057                              '</span></a>' ,
37058                     '</div>',
37059                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37060                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37061                  ];
37062         for(var i = 1, len = cols.length; i < len; i++){
37063             c = cols[i];
37064             colcls = 'x-t-' + tid + '-c' +i;
37065             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37066             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37067                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37068                       "</div>");
37069          }
37070          
37071          buf.push(
37072             '</a>',
37073             '<div class="x-clear"></div></div>',
37074             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37075             "</li>");
37076         
37077         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37078             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37079                                 n.nextSibling.ui.getEl(), buf.join(""));
37080         }else{
37081             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37082         }
37083         var el = this.wrap.firstChild;
37084         this.elRow = el;
37085         this.elNode = el.firstChild;
37086         this.ranchor = el.childNodes[1];
37087         this.ctNode = this.wrap.childNodes[1];
37088         var cs = el.firstChild.childNodes;
37089         this.indentNode = cs[0];
37090         this.ecNode = cs[1];
37091         this.iconNode = cs[2];
37092         var index = 3;
37093         if(cb){
37094             this.checkbox = cs[3];
37095             index++;
37096         }
37097         this.anchor = cs[index];
37098         
37099         this.textNode = cs[index].firstChild;
37100         
37101         //el.on("click", this.onClick, this);
37102         //el.on("dblclick", this.onDblClick, this);
37103         
37104         
37105        // console.log(this);
37106     },
37107     initEvents : function(){
37108         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37109         
37110             
37111         var a = this.ranchor;
37112
37113         var el = Roo.get(a);
37114
37115         if(Roo.isOpera){ // opera render bug ignores the CSS
37116             el.setStyle("text-decoration", "none");
37117         }
37118
37119         el.on("click", this.onClick, this);
37120         el.on("dblclick", this.onDblClick, this);
37121         el.on("contextmenu", this.onContextMenu, this);
37122         
37123     },
37124     
37125     /*onSelectedChange : function(state){
37126         if(state){
37127             this.focus();
37128             this.addClass("x-tree-selected");
37129         }else{
37130             //this.blur();
37131             this.removeClass("x-tree-selected");
37132         }
37133     },*/
37134     addClass : function(cls){
37135         if(this.elRow){
37136             Roo.fly(this.elRow).addClass(cls);
37137         }
37138         
37139     },
37140     
37141     
37142     removeClass : function(cls){
37143         if(this.elRow){
37144             Roo.fly(this.elRow).removeClass(cls);
37145         }
37146     }
37147
37148     
37149     
37150 });//<Script type="text/javascript">
37151
37152 /*
37153  * Based on:
37154  * Ext JS Library 1.1.1
37155  * Copyright(c) 2006-2007, Ext JS, LLC.
37156  *
37157  * Originally Released Under LGPL - original licence link has changed is not relivant.
37158  *
37159  * Fork - LGPL
37160  * <script type="text/javascript">
37161  */
37162  
37163
37164 /**
37165  * @class Roo.tree.ColumnTree
37166  * @extends Roo.data.TreePanel
37167  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37168  * @cfg {int} borderWidth  compined right/left border allowance
37169  * @constructor
37170  * @param {String/HTMLElement/Element} el The container element
37171  * @param {Object} config
37172  */
37173 Roo.tree.ColumnTree =  function(el, config)
37174 {
37175    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37176    this.addEvents({
37177         /**
37178         * @event resize
37179         * Fire this event on a container when it resizes
37180         * @param {int} w Width
37181         * @param {int} h Height
37182         */
37183        "resize" : true
37184     });
37185     this.on('resize', this.onResize, this);
37186 };
37187
37188 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37189     //lines:false,
37190     
37191     
37192     borderWidth: Roo.isBorderBox ? 0 : 2, 
37193     headEls : false,
37194     
37195     render : function(){
37196         // add the header.....
37197        
37198         Roo.tree.ColumnTree.superclass.render.apply(this);
37199         
37200         this.el.addClass('x-column-tree');
37201         
37202         this.headers = this.el.createChild(
37203             {cls:'x-tree-headers'},this.innerCt.dom);
37204    
37205         var cols = this.columns, c;
37206         var totalWidth = 0;
37207         this.headEls = [];
37208         var  len = cols.length;
37209         for(var i = 0; i < len; i++){
37210              c = cols[i];
37211              totalWidth += c.width;
37212             this.headEls.push(this.headers.createChild({
37213                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37214                  cn: {
37215                      cls:'x-tree-hd-text',
37216                      html: c.header
37217                  },
37218                  style:'width:'+(c.width-this.borderWidth)+'px;'
37219              }));
37220         }
37221         this.headers.createChild({cls:'x-clear'});
37222         // prevent floats from wrapping when clipped
37223         this.headers.setWidth(totalWidth);
37224         //this.innerCt.setWidth(totalWidth);
37225         this.innerCt.setStyle({ overflow: 'auto' });
37226         this.onResize(this.width, this.height);
37227              
37228         
37229     },
37230     onResize : function(w,h)
37231     {
37232         this.height = h;
37233         this.width = w;
37234         // resize cols..
37235         this.innerCt.setWidth(this.width);
37236         this.innerCt.setHeight(this.height-20);
37237         
37238         // headers...
37239         var cols = this.columns, c;
37240         var totalWidth = 0;
37241         var expEl = false;
37242         var len = cols.length;
37243         for(var i = 0; i < len; i++){
37244             c = cols[i];
37245             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37246                 // it's the expander..
37247                 expEl  = this.headEls[i];
37248                 continue;
37249             }
37250             totalWidth += c.width;
37251             
37252         }
37253         if (expEl) {
37254             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37255         }
37256         this.headers.setWidth(w-20);
37257
37258         
37259         
37260         
37261     }
37262 });
37263 /*
37264  * Based on:
37265  * Ext JS Library 1.1.1
37266  * Copyright(c) 2006-2007, Ext JS, LLC.
37267  *
37268  * Originally Released Under LGPL - original licence link has changed is not relivant.
37269  *
37270  * Fork - LGPL
37271  * <script type="text/javascript">
37272  */
37273  
37274 /**
37275  * @class Roo.menu.Menu
37276  * @extends Roo.util.Observable
37277  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37278  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37279  * @constructor
37280  * Creates a new Menu
37281  * @param {Object} config Configuration options
37282  */
37283 Roo.menu.Menu = function(config){
37284     
37285     Roo.menu.Menu.superclass.constructor.call(this, config);
37286     
37287     this.id = this.id || Roo.id();
37288     this.addEvents({
37289         /**
37290          * @event beforeshow
37291          * Fires before this menu is displayed
37292          * @param {Roo.menu.Menu} this
37293          */
37294         beforeshow : true,
37295         /**
37296          * @event beforehide
37297          * Fires before this menu is hidden
37298          * @param {Roo.menu.Menu} this
37299          */
37300         beforehide : true,
37301         /**
37302          * @event show
37303          * Fires after this menu is displayed
37304          * @param {Roo.menu.Menu} this
37305          */
37306         show : true,
37307         /**
37308          * @event hide
37309          * Fires after this menu is hidden
37310          * @param {Roo.menu.Menu} this
37311          */
37312         hide : true,
37313         /**
37314          * @event click
37315          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37316          * @param {Roo.menu.Menu} this
37317          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37318          * @param {Roo.EventObject} e
37319          */
37320         click : true,
37321         /**
37322          * @event mouseover
37323          * Fires when the mouse is hovering over this menu
37324          * @param {Roo.menu.Menu} this
37325          * @param {Roo.EventObject} e
37326          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37327          */
37328         mouseover : true,
37329         /**
37330          * @event mouseout
37331          * Fires when the mouse exits this menu
37332          * @param {Roo.menu.Menu} this
37333          * @param {Roo.EventObject} e
37334          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37335          */
37336         mouseout : true,
37337         /**
37338          * @event itemclick
37339          * Fires when a menu item contained in this menu is clicked
37340          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37341          * @param {Roo.EventObject} e
37342          */
37343         itemclick: true
37344     });
37345     if (this.registerMenu) {
37346         Roo.menu.MenuMgr.register(this);
37347     }
37348     
37349     var mis = this.items;
37350     this.items = new Roo.util.MixedCollection();
37351     if(mis){
37352         this.add.apply(this, mis);
37353     }
37354 };
37355
37356 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37357     /**
37358      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37359      */
37360     minWidth : 120,
37361     /**
37362      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37363      * for bottom-right shadow (defaults to "sides")
37364      */
37365     shadow : "sides",
37366     /**
37367      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37368      * this menu (defaults to "tl-tr?")
37369      */
37370     subMenuAlign : "tl-tr?",
37371     /**
37372      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37373      * relative to its element of origin (defaults to "tl-bl?")
37374      */
37375     defaultAlign : "tl-bl?",
37376     /**
37377      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37378      */
37379     allowOtherMenus : false,
37380     /**
37381      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37382      */
37383     registerMenu : true,
37384
37385     hidden:true,
37386
37387     // private
37388     render : function(){
37389         if(this.el){
37390             return;
37391         }
37392         var el = this.el = new Roo.Layer({
37393             cls: "x-menu",
37394             shadow:this.shadow,
37395             constrain: false,
37396             parentEl: this.parentEl || document.body,
37397             zindex:15000
37398         });
37399
37400         this.keyNav = new Roo.menu.MenuNav(this);
37401
37402         if(this.plain){
37403             el.addClass("x-menu-plain");
37404         }
37405         if(this.cls){
37406             el.addClass(this.cls);
37407         }
37408         // generic focus element
37409         this.focusEl = el.createChild({
37410             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37411         });
37412         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37413         //disabling touch- as it's causing issues ..
37414         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37415         ul.on('click'   , this.onClick, this);
37416         
37417         
37418         ul.on("mouseover", this.onMouseOver, this);
37419         ul.on("mouseout", this.onMouseOut, this);
37420         this.items.each(function(item){
37421             if (item.hidden) {
37422                 return;
37423             }
37424             
37425             var li = document.createElement("li");
37426             li.className = "x-menu-list-item";
37427             ul.dom.appendChild(li);
37428             item.render(li, this);
37429         }, this);
37430         this.ul = ul;
37431         this.autoWidth();
37432     },
37433
37434     // private
37435     autoWidth : function(){
37436         var el = this.el, ul = this.ul;
37437         if(!el){
37438             return;
37439         }
37440         var w = this.width;
37441         if(w){
37442             el.setWidth(w);
37443         }else if(Roo.isIE){
37444             el.setWidth(this.minWidth);
37445             var t = el.dom.offsetWidth; // force recalc
37446             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37447         }
37448     },
37449
37450     // private
37451     delayAutoWidth : function(){
37452         if(this.rendered){
37453             if(!this.awTask){
37454                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37455             }
37456             this.awTask.delay(20);
37457         }
37458     },
37459
37460     // private
37461     findTargetItem : function(e){
37462         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37463         if(t && t.menuItemId){
37464             return this.items.get(t.menuItemId);
37465         }
37466     },
37467
37468     // private
37469     onClick : function(e){
37470         Roo.log("menu.onClick");
37471         var t = this.findTargetItem(e);
37472         if(!t){
37473             return;
37474         }
37475         Roo.log(e);
37476         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37477             if(t == this.activeItem && t.shouldDeactivate(e)){
37478                 this.activeItem.deactivate();
37479                 delete this.activeItem;
37480                 return;
37481             }
37482             if(t.canActivate){
37483                 this.setActiveItem(t, true);
37484             }
37485             return;
37486             
37487             
37488         }
37489         
37490         t.onClick(e);
37491         this.fireEvent("click", this, t, e);
37492     },
37493
37494     // private
37495     setActiveItem : function(item, autoExpand){
37496         if(item != this.activeItem){
37497             if(this.activeItem){
37498                 this.activeItem.deactivate();
37499             }
37500             this.activeItem = item;
37501             item.activate(autoExpand);
37502         }else if(autoExpand){
37503             item.expandMenu();
37504         }
37505     },
37506
37507     // private
37508     tryActivate : function(start, step){
37509         var items = this.items;
37510         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37511             var item = items.get(i);
37512             if(!item.disabled && item.canActivate){
37513                 this.setActiveItem(item, false);
37514                 return item;
37515             }
37516         }
37517         return false;
37518     },
37519
37520     // private
37521     onMouseOver : function(e){
37522         var t;
37523         if(t = this.findTargetItem(e)){
37524             if(t.canActivate && !t.disabled){
37525                 this.setActiveItem(t, true);
37526             }
37527         }
37528         this.fireEvent("mouseover", this, e, t);
37529     },
37530
37531     // private
37532     onMouseOut : function(e){
37533         var t;
37534         if(t = this.findTargetItem(e)){
37535             if(t == this.activeItem && t.shouldDeactivate(e)){
37536                 this.activeItem.deactivate();
37537                 delete this.activeItem;
37538             }
37539         }
37540         this.fireEvent("mouseout", this, e, t);
37541     },
37542
37543     /**
37544      * Read-only.  Returns true if the menu is currently displayed, else false.
37545      * @type Boolean
37546      */
37547     isVisible : function(){
37548         return this.el && !this.hidden;
37549     },
37550
37551     /**
37552      * Displays this menu relative to another element
37553      * @param {String/HTMLElement/Roo.Element} element The element to align to
37554      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37555      * the element (defaults to this.defaultAlign)
37556      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37557      */
37558     show : function(el, pos, parentMenu){
37559         this.parentMenu = parentMenu;
37560         if(!this.el){
37561             this.render();
37562         }
37563         this.fireEvent("beforeshow", this);
37564         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37565     },
37566
37567     /**
37568      * Displays this menu at a specific xy position
37569      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37570      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37571      */
37572     showAt : function(xy, parentMenu, /* private: */_e){
37573         this.parentMenu = parentMenu;
37574         if(!this.el){
37575             this.render();
37576         }
37577         if(_e !== false){
37578             this.fireEvent("beforeshow", this);
37579             xy = this.el.adjustForConstraints(xy);
37580         }
37581         this.el.setXY(xy);
37582         this.el.show();
37583         this.hidden = false;
37584         this.focus();
37585         this.fireEvent("show", this);
37586     },
37587
37588     focus : function(){
37589         if(!this.hidden){
37590             this.doFocus.defer(50, this);
37591         }
37592     },
37593
37594     doFocus : function(){
37595         if(!this.hidden){
37596             this.focusEl.focus();
37597         }
37598     },
37599
37600     /**
37601      * Hides this menu and optionally all parent menus
37602      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37603      */
37604     hide : function(deep){
37605         if(this.el && this.isVisible()){
37606             this.fireEvent("beforehide", this);
37607             if(this.activeItem){
37608                 this.activeItem.deactivate();
37609                 this.activeItem = null;
37610             }
37611             this.el.hide();
37612             this.hidden = true;
37613             this.fireEvent("hide", this);
37614         }
37615         if(deep === true && this.parentMenu){
37616             this.parentMenu.hide(true);
37617         }
37618     },
37619
37620     /**
37621      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37622      * Any of the following are valid:
37623      * <ul>
37624      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37625      * <li>An HTMLElement object which will be converted to a menu item</li>
37626      * <li>A menu item config object that will be created as a new menu item</li>
37627      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37628      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37629      * </ul>
37630      * Usage:
37631      * <pre><code>
37632 // Create the menu
37633 var menu = new Roo.menu.Menu();
37634
37635 // Create a menu item to add by reference
37636 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37637
37638 // Add a bunch of items at once using different methods.
37639 // Only the last item added will be returned.
37640 var item = menu.add(
37641     menuItem,                // add existing item by ref
37642     'Dynamic Item',          // new TextItem
37643     '-',                     // new separator
37644     { text: 'Config Item' }  // new item by config
37645 );
37646 </code></pre>
37647      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37648      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37649      */
37650     add : function(){
37651         var a = arguments, l = a.length, item;
37652         for(var i = 0; i < l; i++){
37653             var el = a[i];
37654             if ((typeof(el) == "object") && el.xtype && el.xns) {
37655                 el = Roo.factory(el, Roo.menu);
37656             }
37657             
37658             if(el.render){ // some kind of Item
37659                 item = this.addItem(el);
37660             }else if(typeof el == "string"){ // string
37661                 if(el == "separator" || el == "-"){
37662                     item = this.addSeparator();
37663                 }else{
37664                     item = this.addText(el);
37665                 }
37666             }else if(el.tagName || el.el){ // element
37667                 item = this.addElement(el);
37668             }else if(typeof el == "object"){ // must be menu item config?
37669                 item = this.addMenuItem(el);
37670             }
37671         }
37672         return item;
37673     },
37674
37675     /**
37676      * Returns this menu's underlying {@link Roo.Element} object
37677      * @return {Roo.Element} The element
37678      */
37679     getEl : function(){
37680         if(!this.el){
37681             this.render();
37682         }
37683         return this.el;
37684     },
37685
37686     /**
37687      * Adds a separator bar to the menu
37688      * @return {Roo.menu.Item} The menu item that was added
37689      */
37690     addSeparator : function(){
37691         return this.addItem(new Roo.menu.Separator());
37692     },
37693
37694     /**
37695      * Adds an {@link Roo.Element} object to the menu
37696      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37697      * @return {Roo.menu.Item} The menu item that was added
37698      */
37699     addElement : function(el){
37700         return this.addItem(new Roo.menu.BaseItem(el));
37701     },
37702
37703     /**
37704      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37705      * @param {Roo.menu.Item} item The menu item to add
37706      * @return {Roo.menu.Item} The menu item that was added
37707      */
37708     addItem : function(item){
37709         this.items.add(item);
37710         if(this.ul){
37711             var li = document.createElement("li");
37712             li.className = "x-menu-list-item";
37713             this.ul.dom.appendChild(li);
37714             item.render(li, this);
37715             this.delayAutoWidth();
37716         }
37717         return item;
37718     },
37719
37720     /**
37721      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37722      * @param {Object} config A MenuItem config object
37723      * @return {Roo.menu.Item} The menu item that was added
37724      */
37725     addMenuItem : function(config){
37726         if(!(config instanceof Roo.menu.Item)){
37727             if(typeof config.checked == "boolean"){ // must be check menu item config?
37728                 config = new Roo.menu.CheckItem(config);
37729             }else{
37730                 config = new Roo.menu.Item(config);
37731             }
37732         }
37733         return this.addItem(config);
37734     },
37735
37736     /**
37737      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37738      * @param {String} text The text to display in the menu item
37739      * @return {Roo.menu.Item} The menu item that was added
37740      */
37741     addText : function(text){
37742         return this.addItem(new Roo.menu.TextItem({ text : text }));
37743     },
37744
37745     /**
37746      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37747      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37748      * @param {Roo.menu.Item} item The menu item to add
37749      * @return {Roo.menu.Item} The menu item that was added
37750      */
37751     insert : function(index, item){
37752         this.items.insert(index, item);
37753         if(this.ul){
37754             var li = document.createElement("li");
37755             li.className = "x-menu-list-item";
37756             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37757             item.render(li, this);
37758             this.delayAutoWidth();
37759         }
37760         return item;
37761     },
37762
37763     /**
37764      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37765      * @param {Roo.menu.Item} item The menu item to remove
37766      */
37767     remove : function(item){
37768         this.items.removeKey(item.id);
37769         item.destroy();
37770     },
37771
37772     /**
37773      * Removes and destroys all items in the menu
37774      */
37775     removeAll : function(){
37776         var f;
37777         while(f = this.items.first()){
37778             this.remove(f);
37779         }
37780     }
37781 });
37782
37783 // MenuNav is a private utility class used internally by the Menu
37784 Roo.menu.MenuNav = function(menu){
37785     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37786     this.scope = this.menu = menu;
37787 };
37788
37789 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37790     doRelay : function(e, h){
37791         var k = e.getKey();
37792         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37793             this.menu.tryActivate(0, 1);
37794             return false;
37795         }
37796         return h.call(this.scope || this, e, this.menu);
37797     },
37798
37799     up : function(e, m){
37800         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37801             m.tryActivate(m.items.length-1, -1);
37802         }
37803     },
37804
37805     down : function(e, m){
37806         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37807             m.tryActivate(0, 1);
37808         }
37809     },
37810
37811     right : function(e, m){
37812         if(m.activeItem){
37813             m.activeItem.expandMenu(true);
37814         }
37815     },
37816
37817     left : function(e, m){
37818         m.hide();
37819         if(m.parentMenu && m.parentMenu.activeItem){
37820             m.parentMenu.activeItem.activate();
37821         }
37822     },
37823
37824     enter : function(e, m){
37825         if(m.activeItem){
37826             e.stopPropagation();
37827             m.activeItem.onClick(e);
37828             m.fireEvent("click", this, m.activeItem);
37829             return true;
37830         }
37831     }
37832 });/*
37833  * Based on:
37834  * Ext JS Library 1.1.1
37835  * Copyright(c) 2006-2007, Ext JS, LLC.
37836  *
37837  * Originally Released Under LGPL - original licence link has changed is not relivant.
37838  *
37839  * Fork - LGPL
37840  * <script type="text/javascript">
37841  */
37842  
37843 /**
37844  * @class Roo.menu.MenuMgr
37845  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37846  * @singleton
37847  */
37848 Roo.menu.MenuMgr = function(){
37849    var menus, active, groups = {}, attached = false, lastShow = new Date();
37850
37851    // private - called when first menu is created
37852    function init(){
37853        menus = {};
37854        active = new Roo.util.MixedCollection();
37855        Roo.get(document).addKeyListener(27, function(){
37856            if(active.length > 0){
37857                hideAll();
37858            }
37859        });
37860    }
37861
37862    // private
37863    function hideAll(){
37864        if(active && active.length > 0){
37865            var c = active.clone();
37866            c.each(function(m){
37867                m.hide();
37868            });
37869        }
37870    }
37871
37872    // private
37873    function onHide(m){
37874        active.remove(m);
37875        if(active.length < 1){
37876            Roo.get(document).un("mousedown", onMouseDown);
37877            attached = false;
37878        }
37879    }
37880
37881    // private
37882    function onShow(m){
37883        var last = active.last();
37884        lastShow = new Date();
37885        active.add(m);
37886        if(!attached){
37887            Roo.get(document).on("mousedown", onMouseDown);
37888            attached = true;
37889        }
37890        if(m.parentMenu){
37891           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37892           m.parentMenu.activeChild = m;
37893        }else if(last && last.isVisible()){
37894           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37895        }
37896    }
37897
37898    // private
37899    function onBeforeHide(m){
37900        if(m.activeChild){
37901            m.activeChild.hide();
37902        }
37903        if(m.autoHideTimer){
37904            clearTimeout(m.autoHideTimer);
37905            delete m.autoHideTimer;
37906        }
37907    }
37908
37909    // private
37910    function onBeforeShow(m){
37911        var pm = m.parentMenu;
37912        if(!pm && !m.allowOtherMenus){
37913            hideAll();
37914        }else if(pm && pm.activeChild && active != m){
37915            pm.activeChild.hide();
37916        }
37917    }
37918
37919    // private
37920    function onMouseDown(e){
37921        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37922            hideAll();
37923        }
37924    }
37925
37926    // private
37927    function onBeforeCheck(mi, state){
37928        if(state){
37929            var g = groups[mi.group];
37930            for(var i = 0, l = g.length; i < l; i++){
37931                if(g[i] != mi){
37932                    g[i].setChecked(false);
37933                }
37934            }
37935        }
37936    }
37937
37938    return {
37939
37940        /**
37941         * Hides all menus that are currently visible
37942         */
37943        hideAll : function(){
37944             hideAll();  
37945        },
37946
37947        // private
37948        register : function(menu){
37949            if(!menus){
37950                init();
37951            }
37952            menus[menu.id] = menu;
37953            menu.on("beforehide", onBeforeHide);
37954            menu.on("hide", onHide);
37955            menu.on("beforeshow", onBeforeShow);
37956            menu.on("show", onShow);
37957            var g = menu.group;
37958            if(g && menu.events["checkchange"]){
37959                if(!groups[g]){
37960                    groups[g] = [];
37961                }
37962                groups[g].push(menu);
37963                menu.on("checkchange", onCheck);
37964            }
37965        },
37966
37967         /**
37968          * Returns a {@link Roo.menu.Menu} object
37969          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37970          * be used to generate and return a new Menu instance.
37971          */
37972        get : function(menu){
37973            if(typeof menu == "string"){ // menu id
37974                return menus[menu];
37975            }else if(menu.events){  // menu instance
37976                return menu;
37977            }else if(typeof menu.length == 'number'){ // array of menu items?
37978                return new Roo.menu.Menu({items:menu});
37979            }else{ // otherwise, must be a config
37980                return new Roo.menu.Menu(menu);
37981            }
37982        },
37983
37984        // private
37985        unregister : function(menu){
37986            delete menus[menu.id];
37987            menu.un("beforehide", onBeforeHide);
37988            menu.un("hide", onHide);
37989            menu.un("beforeshow", onBeforeShow);
37990            menu.un("show", onShow);
37991            var g = menu.group;
37992            if(g && menu.events["checkchange"]){
37993                groups[g].remove(menu);
37994                menu.un("checkchange", onCheck);
37995            }
37996        },
37997
37998        // private
37999        registerCheckable : function(menuItem){
38000            var g = menuItem.group;
38001            if(g){
38002                if(!groups[g]){
38003                    groups[g] = [];
38004                }
38005                groups[g].push(menuItem);
38006                menuItem.on("beforecheckchange", onBeforeCheck);
38007            }
38008        },
38009
38010        // private
38011        unregisterCheckable : function(menuItem){
38012            var g = menuItem.group;
38013            if(g){
38014                groups[g].remove(menuItem);
38015                menuItem.un("beforecheckchange", onBeforeCheck);
38016            }
38017        }
38018    };
38019 }();/*
38020  * Based on:
38021  * Ext JS Library 1.1.1
38022  * Copyright(c) 2006-2007, Ext JS, LLC.
38023  *
38024  * Originally Released Under LGPL - original licence link has changed is not relivant.
38025  *
38026  * Fork - LGPL
38027  * <script type="text/javascript">
38028  */
38029  
38030
38031 /**
38032  * @class Roo.menu.BaseItem
38033  * @extends Roo.Component
38034  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38035  * management and base configuration options shared by all menu components.
38036  * @constructor
38037  * Creates a new BaseItem
38038  * @param {Object} config Configuration options
38039  */
38040 Roo.menu.BaseItem = function(config){
38041     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38042
38043     this.addEvents({
38044         /**
38045          * @event click
38046          * Fires when this item is clicked
38047          * @param {Roo.menu.BaseItem} this
38048          * @param {Roo.EventObject} e
38049          */
38050         click: true,
38051         /**
38052          * @event activate
38053          * Fires when this item is activated
38054          * @param {Roo.menu.BaseItem} this
38055          */
38056         activate : true,
38057         /**
38058          * @event deactivate
38059          * Fires when this item is deactivated
38060          * @param {Roo.menu.BaseItem} this
38061          */
38062         deactivate : true
38063     });
38064
38065     if(this.handler){
38066         this.on("click", this.handler, this.scope, true);
38067     }
38068 };
38069
38070 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38071     /**
38072      * @cfg {Function} handler
38073      * A function that will handle the click event of this menu item (defaults to undefined)
38074      */
38075     /**
38076      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38077      */
38078     canActivate : false,
38079     
38080      /**
38081      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38082      */
38083     hidden: false,
38084     
38085     /**
38086      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38087      */
38088     activeClass : "x-menu-item-active",
38089     /**
38090      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38091      */
38092     hideOnClick : true,
38093     /**
38094      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38095      */
38096     hideDelay : 100,
38097
38098     // private
38099     ctype: "Roo.menu.BaseItem",
38100
38101     // private
38102     actionMode : "container",
38103
38104     // private
38105     render : function(container, parentMenu){
38106         this.parentMenu = parentMenu;
38107         Roo.menu.BaseItem.superclass.render.call(this, container);
38108         this.container.menuItemId = this.id;
38109     },
38110
38111     // private
38112     onRender : function(container, position){
38113         this.el = Roo.get(this.el);
38114         container.dom.appendChild(this.el.dom);
38115     },
38116
38117     // private
38118     onClick : function(e){
38119         if(!this.disabled && this.fireEvent("click", this, e) !== false
38120                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38121             this.handleClick(e);
38122         }else{
38123             e.stopEvent();
38124         }
38125     },
38126
38127     // private
38128     activate : function(){
38129         if(this.disabled){
38130             return false;
38131         }
38132         var li = this.container;
38133         li.addClass(this.activeClass);
38134         this.region = li.getRegion().adjust(2, 2, -2, -2);
38135         this.fireEvent("activate", this);
38136         return true;
38137     },
38138
38139     // private
38140     deactivate : function(){
38141         this.container.removeClass(this.activeClass);
38142         this.fireEvent("deactivate", this);
38143     },
38144
38145     // private
38146     shouldDeactivate : function(e){
38147         return !this.region || !this.region.contains(e.getPoint());
38148     },
38149
38150     // private
38151     handleClick : function(e){
38152         if(this.hideOnClick){
38153             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38154         }
38155     },
38156
38157     // private
38158     expandMenu : function(autoActivate){
38159         // do nothing
38160     },
38161
38162     // private
38163     hideMenu : function(){
38164         // do nothing
38165     }
38166 });/*
38167  * Based on:
38168  * Ext JS Library 1.1.1
38169  * Copyright(c) 2006-2007, Ext JS, LLC.
38170  *
38171  * Originally Released Under LGPL - original licence link has changed is not relivant.
38172  *
38173  * Fork - LGPL
38174  * <script type="text/javascript">
38175  */
38176  
38177 /**
38178  * @class Roo.menu.Adapter
38179  * @extends Roo.menu.BaseItem
38180  * 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.
38181  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38182  * @constructor
38183  * Creates a new Adapter
38184  * @param {Object} config Configuration options
38185  */
38186 Roo.menu.Adapter = function(component, config){
38187     Roo.menu.Adapter.superclass.constructor.call(this, config);
38188     this.component = component;
38189 };
38190 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38191     // private
38192     canActivate : true,
38193
38194     // private
38195     onRender : function(container, position){
38196         this.component.render(container);
38197         this.el = this.component.getEl();
38198     },
38199
38200     // private
38201     activate : function(){
38202         if(this.disabled){
38203             return false;
38204         }
38205         this.component.focus();
38206         this.fireEvent("activate", this);
38207         return true;
38208     },
38209
38210     // private
38211     deactivate : function(){
38212         this.fireEvent("deactivate", this);
38213     },
38214
38215     // private
38216     disable : function(){
38217         this.component.disable();
38218         Roo.menu.Adapter.superclass.disable.call(this);
38219     },
38220
38221     // private
38222     enable : function(){
38223         this.component.enable();
38224         Roo.menu.Adapter.superclass.enable.call(this);
38225     }
38226 });/*
38227  * Based on:
38228  * Ext JS Library 1.1.1
38229  * Copyright(c) 2006-2007, Ext JS, LLC.
38230  *
38231  * Originally Released Under LGPL - original licence link has changed is not relivant.
38232  *
38233  * Fork - LGPL
38234  * <script type="text/javascript">
38235  */
38236
38237 /**
38238  * @class Roo.menu.TextItem
38239  * @extends Roo.menu.BaseItem
38240  * Adds a static text string to a menu, usually used as either a heading or group separator.
38241  * Note: old style constructor with text is still supported.
38242  * 
38243  * @constructor
38244  * Creates a new TextItem
38245  * @param {Object} cfg Configuration
38246  */
38247 Roo.menu.TextItem = function(cfg){
38248     if (typeof(cfg) == 'string') {
38249         this.text = cfg;
38250     } else {
38251         Roo.apply(this,cfg);
38252     }
38253     
38254     Roo.menu.TextItem.superclass.constructor.call(this);
38255 };
38256
38257 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38258     /**
38259      * @cfg {Boolean} text Text to show on item.
38260      */
38261     text : '',
38262     
38263     /**
38264      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38265      */
38266     hideOnClick : false,
38267     /**
38268      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38269      */
38270     itemCls : "x-menu-text",
38271
38272     // private
38273     onRender : function(){
38274         var s = document.createElement("span");
38275         s.className = this.itemCls;
38276         s.innerHTML = this.text;
38277         this.el = s;
38278         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38279     }
38280 });/*
38281  * Based on:
38282  * Ext JS Library 1.1.1
38283  * Copyright(c) 2006-2007, Ext JS, LLC.
38284  *
38285  * Originally Released Under LGPL - original licence link has changed is not relivant.
38286  *
38287  * Fork - LGPL
38288  * <script type="text/javascript">
38289  */
38290
38291 /**
38292  * @class Roo.menu.Separator
38293  * @extends Roo.menu.BaseItem
38294  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38295  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38296  * @constructor
38297  * @param {Object} config Configuration options
38298  */
38299 Roo.menu.Separator = function(config){
38300     Roo.menu.Separator.superclass.constructor.call(this, config);
38301 };
38302
38303 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38304     /**
38305      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38306      */
38307     itemCls : "x-menu-sep",
38308     /**
38309      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38310      */
38311     hideOnClick : false,
38312
38313     // private
38314     onRender : function(li){
38315         var s = document.createElement("span");
38316         s.className = this.itemCls;
38317         s.innerHTML = "&#160;";
38318         this.el = s;
38319         li.addClass("x-menu-sep-li");
38320         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38321     }
38322 });/*
38323  * Based on:
38324  * Ext JS Library 1.1.1
38325  * Copyright(c) 2006-2007, Ext JS, LLC.
38326  *
38327  * Originally Released Under LGPL - original licence link has changed is not relivant.
38328  *
38329  * Fork - LGPL
38330  * <script type="text/javascript">
38331  */
38332 /**
38333  * @class Roo.menu.Item
38334  * @extends Roo.menu.BaseItem
38335  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38336  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38337  * activation and click handling.
38338  * @constructor
38339  * Creates a new Item
38340  * @param {Object} config Configuration options
38341  */
38342 Roo.menu.Item = function(config){
38343     Roo.menu.Item.superclass.constructor.call(this, config);
38344     if(this.menu){
38345         this.menu = Roo.menu.MenuMgr.get(this.menu);
38346     }
38347 };
38348 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38349     
38350     /**
38351      * @cfg {String} text
38352      * The text to show on the menu item.
38353      */
38354     text: '',
38355      /**
38356      * @cfg {String} HTML to render in menu
38357      * The text to show on the menu item (HTML version).
38358      */
38359     html: '',
38360     /**
38361      * @cfg {String} icon
38362      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38363      */
38364     icon: undefined,
38365     /**
38366      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38367      */
38368     itemCls : "x-menu-item",
38369     /**
38370      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38371      */
38372     canActivate : true,
38373     /**
38374      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38375      */
38376     showDelay: 200,
38377     // doc'd in BaseItem
38378     hideDelay: 200,
38379
38380     // private
38381     ctype: "Roo.menu.Item",
38382     
38383     // private
38384     onRender : function(container, position){
38385         var el = document.createElement("a");
38386         el.hideFocus = true;
38387         el.unselectable = "on";
38388         el.href = this.href || "#";
38389         if(this.hrefTarget){
38390             el.target = this.hrefTarget;
38391         }
38392         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38393         
38394         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38395         
38396         el.innerHTML = String.format(
38397                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38398                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38399         this.el = el;
38400         Roo.menu.Item.superclass.onRender.call(this, container, position);
38401     },
38402
38403     /**
38404      * Sets the text to display in this menu item
38405      * @param {String} text The text to display
38406      * @param {Boolean} isHTML true to indicate text is pure html.
38407      */
38408     setText : function(text, isHTML){
38409         if (isHTML) {
38410             this.html = text;
38411         } else {
38412             this.text = text;
38413             this.html = '';
38414         }
38415         if(this.rendered){
38416             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38417      
38418             this.el.update(String.format(
38419                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38420                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38421             this.parentMenu.autoWidth();
38422         }
38423     },
38424
38425     // private
38426     handleClick : function(e){
38427         if(!this.href){ // if no link defined, stop the event automatically
38428             e.stopEvent();
38429         }
38430         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38431     },
38432
38433     // private
38434     activate : function(autoExpand){
38435         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38436             this.focus();
38437             if(autoExpand){
38438                 this.expandMenu();
38439             }
38440         }
38441         return true;
38442     },
38443
38444     // private
38445     shouldDeactivate : function(e){
38446         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38447             if(this.menu && this.menu.isVisible()){
38448                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38449             }
38450             return true;
38451         }
38452         return false;
38453     },
38454
38455     // private
38456     deactivate : function(){
38457         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38458         this.hideMenu();
38459     },
38460
38461     // private
38462     expandMenu : function(autoActivate){
38463         if(!this.disabled && this.menu){
38464             clearTimeout(this.hideTimer);
38465             delete this.hideTimer;
38466             if(!this.menu.isVisible() && !this.showTimer){
38467                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38468             }else if (this.menu.isVisible() && autoActivate){
38469                 this.menu.tryActivate(0, 1);
38470             }
38471         }
38472     },
38473
38474     // private
38475     deferExpand : function(autoActivate){
38476         delete this.showTimer;
38477         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38478         if(autoActivate){
38479             this.menu.tryActivate(0, 1);
38480         }
38481     },
38482
38483     // private
38484     hideMenu : function(){
38485         clearTimeout(this.showTimer);
38486         delete this.showTimer;
38487         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38488             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38489         }
38490     },
38491
38492     // private
38493     deferHide : function(){
38494         delete this.hideTimer;
38495         this.menu.hide();
38496     }
38497 });/*
38498  * Based on:
38499  * Ext JS Library 1.1.1
38500  * Copyright(c) 2006-2007, Ext JS, LLC.
38501  *
38502  * Originally Released Under LGPL - original licence link has changed is not relivant.
38503  *
38504  * Fork - LGPL
38505  * <script type="text/javascript">
38506  */
38507  
38508 /**
38509  * @class Roo.menu.CheckItem
38510  * @extends Roo.menu.Item
38511  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38512  * @constructor
38513  * Creates a new CheckItem
38514  * @param {Object} config Configuration options
38515  */
38516 Roo.menu.CheckItem = function(config){
38517     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38518     this.addEvents({
38519         /**
38520          * @event beforecheckchange
38521          * Fires before the checked value is set, providing an opportunity to cancel if needed
38522          * @param {Roo.menu.CheckItem} this
38523          * @param {Boolean} checked The new checked value that will be set
38524          */
38525         "beforecheckchange" : true,
38526         /**
38527          * @event checkchange
38528          * Fires after the checked value has been set
38529          * @param {Roo.menu.CheckItem} this
38530          * @param {Boolean} checked The checked value that was set
38531          */
38532         "checkchange" : true
38533     });
38534     if(this.checkHandler){
38535         this.on('checkchange', this.checkHandler, this.scope);
38536     }
38537 };
38538 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38539     /**
38540      * @cfg {String} group
38541      * All check items with the same group name will automatically be grouped into a single-select
38542      * radio button group (defaults to '')
38543      */
38544     /**
38545      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38546      */
38547     itemCls : "x-menu-item x-menu-check-item",
38548     /**
38549      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38550      */
38551     groupClass : "x-menu-group-item",
38552
38553     /**
38554      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38555      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38556      * initialized with checked = true will be rendered as checked.
38557      */
38558     checked: false,
38559
38560     // private
38561     ctype: "Roo.menu.CheckItem",
38562
38563     // private
38564     onRender : function(c){
38565         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38566         if(this.group){
38567             this.el.addClass(this.groupClass);
38568         }
38569         Roo.menu.MenuMgr.registerCheckable(this);
38570         if(this.checked){
38571             this.checked = false;
38572             this.setChecked(true, true);
38573         }
38574     },
38575
38576     // private
38577     destroy : function(){
38578         if(this.rendered){
38579             Roo.menu.MenuMgr.unregisterCheckable(this);
38580         }
38581         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38582     },
38583
38584     /**
38585      * Set the checked state of this item
38586      * @param {Boolean} checked The new checked value
38587      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38588      */
38589     setChecked : function(state, suppressEvent){
38590         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38591             if(this.container){
38592                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38593             }
38594             this.checked = state;
38595             if(suppressEvent !== true){
38596                 this.fireEvent("checkchange", this, state);
38597             }
38598         }
38599     },
38600
38601     // private
38602     handleClick : function(e){
38603        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38604            this.setChecked(!this.checked);
38605        }
38606        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38607     }
38608 });/*
38609  * Based on:
38610  * Ext JS Library 1.1.1
38611  * Copyright(c) 2006-2007, Ext JS, LLC.
38612  *
38613  * Originally Released Under LGPL - original licence link has changed is not relivant.
38614  *
38615  * Fork - LGPL
38616  * <script type="text/javascript">
38617  */
38618  
38619 /**
38620  * @class Roo.menu.DateItem
38621  * @extends Roo.menu.Adapter
38622  * A menu item that wraps the {@link Roo.DatPicker} component.
38623  * @constructor
38624  * Creates a new DateItem
38625  * @param {Object} config Configuration options
38626  */
38627 Roo.menu.DateItem = function(config){
38628     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38629     /** The Roo.DatePicker object @type Roo.DatePicker */
38630     this.picker = this.component;
38631     this.addEvents({select: true});
38632     
38633     this.picker.on("render", function(picker){
38634         picker.getEl().swallowEvent("click");
38635         picker.container.addClass("x-menu-date-item");
38636     });
38637
38638     this.picker.on("select", this.onSelect, this);
38639 };
38640
38641 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38642     // private
38643     onSelect : function(picker, date){
38644         this.fireEvent("select", this, date, picker);
38645         Roo.menu.DateItem.superclass.handleClick.call(this);
38646     }
38647 });/*
38648  * Based on:
38649  * Ext JS Library 1.1.1
38650  * Copyright(c) 2006-2007, Ext JS, LLC.
38651  *
38652  * Originally Released Under LGPL - original licence link has changed is not relivant.
38653  *
38654  * Fork - LGPL
38655  * <script type="text/javascript">
38656  */
38657  
38658 /**
38659  * @class Roo.menu.ColorItem
38660  * @extends Roo.menu.Adapter
38661  * A menu item that wraps the {@link Roo.ColorPalette} component.
38662  * @constructor
38663  * Creates a new ColorItem
38664  * @param {Object} config Configuration options
38665  */
38666 Roo.menu.ColorItem = function(config){
38667     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38668     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38669     this.palette = this.component;
38670     this.relayEvents(this.palette, ["select"]);
38671     if(this.selectHandler){
38672         this.on('select', this.selectHandler, this.scope);
38673     }
38674 };
38675 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38676  * Based on:
38677  * Ext JS Library 1.1.1
38678  * Copyright(c) 2006-2007, Ext JS, LLC.
38679  *
38680  * Originally Released Under LGPL - original licence link has changed is not relivant.
38681  *
38682  * Fork - LGPL
38683  * <script type="text/javascript">
38684  */
38685  
38686
38687 /**
38688  * @class Roo.menu.DateMenu
38689  * @extends Roo.menu.Menu
38690  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38691  * @constructor
38692  * Creates a new DateMenu
38693  * @param {Object} config Configuration options
38694  */
38695 Roo.menu.DateMenu = function(config){
38696     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38697     this.plain = true;
38698     var di = new Roo.menu.DateItem(config);
38699     this.add(di);
38700     /**
38701      * The {@link Roo.DatePicker} instance for this DateMenu
38702      * @type DatePicker
38703      */
38704     this.picker = di.picker;
38705     /**
38706      * @event select
38707      * @param {DatePicker} picker
38708      * @param {Date} date
38709      */
38710     this.relayEvents(di, ["select"]);
38711     this.on('beforeshow', function(){
38712         if(this.picker){
38713             this.picker.hideMonthPicker(false);
38714         }
38715     }, this);
38716 };
38717 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38718     cls:'x-date-menu'
38719 });/*
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 /**
38732  * @class Roo.menu.ColorMenu
38733  * @extends Roo.menu.Menu
38734  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38735  * @constructor
38736  * Creates a new ColorMenu
38737  * @param {Object} config Configuration options
38738  */
38739 Roo.menu.ColorMenu = function(config){
38740     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38741     this.plain = true;
38742     var ci = new Roo.menu.ColorItem(config);
38743     this.add(ci);
38744     /**
38745      * The {@link Roo.ColorPalette} instance for this ColorMenu
38746      * @type ColorPalette
38747      */
38748     this.palette = ci.palette;
38749     /**
38750      * @event select
38751      * @param {ColorPalette} palette
38752      * @param {String} color
38753      */
38754     this.relayEvents(ci, ["select"]);
38755 };
38756 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38757  * Based on:
38758  * Ext JS Library 1.1.1
38759  * Copyright(c) 2006-2007, Ext JS, LLC.
38760  *
38761  * Originally Released Under LGPL - original licence link has changed is not relivant.
38762  *
38763  * Fork - LGPL
38764  * <script type="text/javascript">
38765  */
38766  
38767 /**
38768  * @class Roo.form.TextItem
38769  * @extends Roo.BoxComponent
38770  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38771  * @constructor
38772  * Creates a new TextItem
38773  * @param {Object} config Configuration options
38774  */
38775 Roo.form.TextItem = function(config){
38776     Roo.form.TextItem.superclass.constructor.call(this, config);
38777 };
38778
38779 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38780     
38781     /**
38782      * @cfg {String} tag the tag for this item (default div)
38783      */
38784     tag : 'div',
38785     /**
38786      * @cfg {String} html the content for this item
38787      */
38788     html : '',
38789     
38790     getAutoCreate : function()
38791     {
38792         var cfg = {
38793             id: this.id,
38794             tag: this.tag,
38795             html: this.html,
38796             cls: 'x-form-item'
38797         };
38798         
38799         return cfg;
38800         
38801     },
38802     
38803     onRender : function(ct, position)
38804     {
38805         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38806         
38807         if(!this.el){
38808             var cfg = this.getAutoCreate();
38809             if(!cfg.name){
38810                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38811             }
38812             if (!cfg.name.length) {
38813                 delete cfg.name;
38814             }
38815             this.el = ct.createChild(cfg, position);
38816         }
38817     }
38818     
38819 });/*
38820  * Based on:
38821  * Ext JS Library 1.1.1
38822  * Copyright(c) 2006-2007, Ext JS, LLC.
38823  *
38824  * Originally Released Under LGPL - original licence link has changed is not relivant.
38825  *
38826  * Fork - LGPL
38827  * <script type="text/javascript">
38828  */
38829  
38830 /**
38831  * @class Roo.form.Field
38832  * @extends Roo.BoxComponent
38833  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38834  * @constructor
38835  * Creates a new Field
38836  * @param {Object} config Configuration options
38837  */
38838 Roo.form.Field = function(config){
38839     Roo.form.Field.superclass.constructor.call(this, config);
38840 };
38841
38842 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38843     /**
38844      * @cfg {String} fieldLabel Label to use when rendering a form.
38845      */
38846        /**
38847      * @cfg {String} qtip Mouse over tip
38848      */
38849      
38850     /**
38851      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38852      */
38853     invalidClass : "x-form-invalid",
38854     /**
38855      * @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")
38856      */
38857     invalidText : "The value in this field is invalid",
38858     /**
38859      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38860      */
38861     focusClass : "x-form-focus",
38862     /**
38863      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38864       automatic validation (defaults to "keyup").
38865      */
38866     validationEvent : "keyup",
38867     /**
38868      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38869      */
38870     validateOnBlur : true,
38871     /**
38872      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38873      */
38874     validationDelay : 250,
38875     /**
38876      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38877      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38878      */
38879     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38880     /**
38881      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38882      */
38883     fieldClass : "x-form-field",
38884     /**
38885      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38886      *<pre>
38887 Value         Description
38888 -----------   ----------------------------------------------------------------------
38889 qtip          Display a quick tip when the user hovers over the field
38890 title         Display a default browser title attribute popup
38891 under         Add a block div beneath the field containing the error text
38892 side          Add an error icon to the right of the field with a popup on hover
38893 [element id]  Add the error text directly to the innerHTML of the specified element
38894 </pre>
38895      */
38896     msgTarget : 'qtip',
38897     /**
38898      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38899      */
38900     msgFx : 'normal',
38901
38902     /**
38903      * @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.
38904      */
38905     readOnly : false,
38906
38907     /**
38908      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38909      */
38910     disabled : false,
38911
38912     /**
38913      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38914      */
38915     inputType : undefined,
38916     
38917     /**
38918      * @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).
38919          */
38920         tabIndex : undefined,
38921         
38922     // private
38923     isFormField : true,
38924
38925     // private
38926     hasFocus : false,
38927     /**
38928      * @property {Roo.Element} fieldEl
38929      * Element Containing the rendered Field (with label etc.)
38930      */
38931     /**
38932      * @cfg {Mixed} value A value to initialize this field with.
38933      */
38934     value : undefined,
38935
38936     /**
38937      * @cfg {String} name The field's HTML name attribute.
38938      */
38939     /**
38940      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38941      */
38942     // private
38943     loadedValue : false,
38944      
38945      
38946         // private ??
38947         initComponent : function(){
38948         Roo.form.Field.superclass.initComponent.call(this);
38949         this.addEvents({
38950             /**
38951              * @event focus
38952              * Fires when this field receives input focus.
38953              * @param {Roo.form.Field} this
38954              */
38955             focus : true,
38956             /**
38957              * @event blur
38958              * Fires when this field loses input focus.
38959              * @param {Roo.form.Field} this
38960              */
38961             blur : true,
38962             /**
38963              * @event specialkey
38964              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38965              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38966              * @param {Roo.form.Field} this
38967              * @param {Roo.EventObject} e The event object
38968              */
38969             specialkey : true,
38970             /**
38971              * @event change
38972              * Fires just before the field blurs if the field value has changed.
38973              * @param {Roo.form.Field} this
38974              * @param {Mixed} newValue The new value
38975              * @param {Mixed} oldValue The original value
38976              */
38977             change : true,
38978             /**
38979              * @event invalid
38980              * Fires after the field has been marked as invalid.
38981              * @param {Roo.form.Field} this
38982              * @param {String} msg The validation message
38983              */
38984             invalid : true,
38985             /**
38986              * @event valid
38987              * Fires after the field has been validated with no errors.
38988              * @param {Roo.form.Field} this
38989              */
38990             valid : true,
38991              /**
38992              * @event keyup
38993              * Fires after the key up
38994              * @param {Roo.form.Field} this
38995              * @param {Roo.EventObject}  e The event Object
38996              */
38997             keyup : true
38998         });
38999     },
39000
39001     /**
39002      * Returns the name attribute of the field if available
39003      * @return {String} name The field name
39004      */
39005     getName: function(){
39006          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39007     },
39008
39009     // private
39010     onRender : function(ct, position){
39011         Roo.form.Field.superclass.onRender.call(this, ct, position);
39012         if(!this.el){
39013             var cfg = this.getAutoCreate();
39014             if(!cfg.name){
39015                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39016             }
39017             if (!cfg.name.length) {
39018                 delete cfg.name;
39019             }
39020             if(this.inputType){
39021                 cfg.type = this.inputType;
39022             }
39023             this.el = ct.createChild(cfg, position);
39024         }
39025         var type = this.el.dom.type;
39026         if(type){
39027             if(type == 'password'){
39028                 type = 'text';
39029             }
39030             this.el.addClass('x-form-'+type);
39031         }
39032         if(this.readOnly){
39033             this.el.dom.readOnly = true;
39034         }
39035         if(this.tabIndex !== undefined){
39036             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39037         }
39038
39039         this.el.addClass([this.fieldClass, this.cls]);
39040         this.initValue();
39041     },
39042
39043     /**
39044      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39045      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39046      * @return {Roo.form.Field} this
39047      */
39048     applyTo : function(target){
39049         this.allowDomMove = false;
39050         this.el = Roo.get(target);
39051         this.render(this.el.dom.parentNode);
39052         return this;
39053     },
39054
39055     // private
39056     initValue : function(){
39057         if(this.value !== undefined){
39058             this.setValue(this.value);
39059         }else if(this.el.dom.value.length > 0){
39060             this.setValue(this.el.dom.value);
39061         }
39062     },
39063
39064     /**
39065      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39066      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39067      */
39068     isDirty : function() {
39069         if(this.disabled) {
39070             return false;
39071         }
39072         return String(this.getValue()) !== String(this.originalValue);
39073     },
39074
39075     /**
39076      * stores the current value in loadedValue
39077      */
39078     resetHasChanged : function()
39079     {
39080         this.loadedValue = String(this.getValue());
39081     },
39082     /**
39083      * checks the current value against the 'loaded' value.
39084      * Note - will return false if 'resetHasChanged' has not been called first.
39085      */
39086     hasChanged : function()
39087     {
39088         if(this.disabled || this.readOnly) {
39089             return false;
39090         }
39091         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39092     },
39093     
39094     
39095     
39096     // private
39097     afterRender : function(){
39098         Roo.form.Field.superclass.afterRender.call(this);
39099         this.initEvents();
39100     },
39101
39102     // private
39103     fireKey : function(e){
39104         //Roo.log('field ' + e.getKey());
39105         if(e.isNavKeyPress()){
39106             this.fireEvent("specialkey", this, e);
39107         }
39108     },
39109
39110     /**
39111      * Resets the current field value to the originally loaded value and clears any validation messages
39112      */
39113     reset : function(){
39114         this.setValue(this.resetValue);
39115         this.originalValue = this.getValue();
39116         this.clearInvalid();
39117     },
39118
39119     // private
39120     initEvents : function(){
39121         // safari killled keypress - so keydown is now used..
39122         this.el.on("keydown" , this.fireKey,  this);
39123         this.el.on("focus", this.onFocus,  this);
39124         this.el.on("blur", this.onBlur,  this);
39125         this.el.relayEvent('keyup', this);
39126
39127         // reference to original value for reset
39128         this.originalValue = this.getValue();
39129         this.resetValue =  this.getValue();
39130     },
39131
39132     // private
39133     onFocus : function(){
39134         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39135             this.el.addClass(this.focusClass);
39136         }
39137         if(!this.hasFocus){
39138             this.hasFocus = true;
39139             this.startValue = this.getValue();
39140             this.fireEvent("focus", this);
39141         }
39142     },
39143
39144     beforeBlur : Roo.emptyFn,
39145
39146     // private
39147     onBlur : function(){
39148         this.beforeBlur();
39149         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39150             this.el.removeClass(this.focusClass);
39151         }
39152         this.hasFocus = false;
39153         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39154             this.validate();
39155         }
39156         var v = this.getValue();
39157         if(String(v) !== String(this.startValue)){
39158             this.fireEvent('change', this, v, this.startValue);
39159         }
39160         this.fireEvent("blur", this);
39161     },
39162
39163     /**
39164      * Returns whether or not the field value is currently valid
39165      * @param {Boolean} preventMark True to disable marking the field invalid
39166      * @return {Boolean} True if the value is valid, else false
39167      */
39168     isValid : function(preventMark){
39169         if(this.disabled){
39170             return true;
39171         }
39172         var restore = this.preventMark;
39173         this.preventMark = preventMark === true;
39174         var v = this.validateValue(this.processValue(this.getRawValue()));
39175         this.preventMark = restore;
39176         return v;
39177     },
39178
39179     /**
39180      * Validates the field value
39181      * @return {Boolean} True if the value is valid, else false
39182      */
39183     validate : function(){
39184         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39185             this.clearInvalid();
39186             return true;
39187         }
39188         return false;
39189     },
39190
39191     processValue : function(value){
39192         return value;
39193     },
39194
39195     // private
39196     // Subclasses should provide the validation implementation by overriding this
39197     validateValue : function(value){
39198         return true;
39199     },
39200
39201     /**
39202      * Mark this field as invalid
39203      * @param {String} msg The validation message
39204      */
39205     markInvalid : function(msg){
39206         if(!this.rendered || this.preventMark){ // not rendered
39207             return;
39208         }
39209         
39210         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39211         
39212         obj.el.addClass(this.invalidClass);
39213         msg = msg || this.invalidText;
39214         switch(this.msgTarget){
39215             case 'qtip':
39216                 obj.el.dom.qtip = msg;
39217                 obj.el.dom.qclass = 'x-form-invalid-tip';
39218                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39219                     Roo.QuickTips.enable();
39220                 }
39221                 break;
39222             case 'title':
39223                 this.el.dom.title = msg;
39224                 break;
39225             case 'under':
39226                 if(!this.errorEl){
39227                     var elp = this.el.findParent('.x-form-element', 5, true);
39228                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39229                     this.errorEl.setWidth(elp.getWidth(true)-20);
39230                 }
39231                 this.errorEl.update(msg);
39232                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39233                 break;
39234             case 'side':
39235                 if(!this.errorIcon){
39236                     var elp = this.el.findParent('.x-form-element', 5, true);
39237                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39238                 }
39239                 this.alignErrorIcon();
39240                 this.errorIcon.dom.qtip = msg;
39241                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39242                 this.errorIcon.show();
39243                 this.on('resize', this.alignErrorIcon, this);
39244                 break;
39245             default:
39246                 var t = Roo.getDom(this.msgTarget);
39247                 t.innerHTML = msg;
39248                 t.style.display = this.msgDisplay;
39249                 break;
39250         }
39251         this.fireEvent('invalid', this, msg);
39252     },
39253
39254     // private
39255     alignErrorIcon : function(){
39256         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39257     },
39258
39259     /**
39260      * Clear any invalid styles/messages for this field
39261      */
39262     clearInvalid : function(){
39263         if(!this.rendered || this.preventMark){ // not rendered
39264             return;
39265         }
39266         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39267         
39268         obj.el.removeClass(this.invalidClass);
39269         switch(this.msgTarget){
39270             case 'qtip':
39271                 obj.el.dom.qtip = '';
39272                 break;
39273             case 'title':
39274                 this.el.dom.title = '';
39275                 break;
39276             case 'under':
39277                 if(this.errorEl){
39278                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39279                 }
39280                 break;
39281             case 'side':
39282                 if(this.errorIcon){
39283                     this.errorIcon.dom.qtip = '';
39284                     this.errorIcon.hide();
39285                     this.un('resize', this.alignErrorIcon, this);
39286                 }
39287                 break;
39288             default:
39289                 var t = Roo.getDom(this.msgTarget);
39290                 t.innerHTML = '';
39291                 t.style.display = 'none';
39292                 break;
39293         }
39294         this.fireEvent('valid', this);
39295     },
39296
39297     /**
39298      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39299      * @return {Mixed} value The field value
39300      */
39301     getRawValue : function(){
39302         var v = this.el.getValue();
39303         
39304         return v;
39305     },
39306
39307     /**
39308      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39309      * @return {Mixed} value The field value
39310      */
39311     getValue : function(){
39312         var v = this.el.getValue();
39313          
39314         return v;
39315     },
39316
39317     /**
39318      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39319      * @param {Mixed} value The value to set
39320      */
39321     setRawValue : function(v){
39322         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39323     },
39324
39325     /**
39326      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39327      * @param {Mixed} value The value to set
39328      */
39329     setValue : function(v){
39330         this.value = v;
39331         if(this.rendered){
39332             this.el.dom.value = (v === null || v === undefined ? '' : v);
39333              this.validate();
39334         }
39335     },
39336
39337     adjustSize : function(w, h){
39338         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39339         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39340         return s;
39341     },
39342
39343     adjustWidth : function(tag, w){
39344         tag = tag.toLowerCase();
39345         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39346             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39347                 if(tag == 'input'){
39348                     return w + 2;
39349                 }
39350                 if(tag == 'textarea'){
39351                     return w-2;
39352                 }
39353             }else if(Roo.isOpera){
39354                 if(tag == 'input'){
39355                     return w + 2;
39356                 }
39357                 if(tag == 'textarea'){
39358                     return w-2;
39359                 }
39360             }
39361         }
39362         return w;
39363     }
39364 });
39365
39366
39367 // anything other than normal should be considered experimental
39368 Roo.form.Field.msgFx = {
39369     normal : {
39370         show: function(msgEl, f){
39371             msgEl.setDisplayed('block');
39372         },
39373
39374         hide : function(msgEl, f){
39375             msgEl.setDisplayed(false).update('');
39376         }
39377     },
39378
39379     slide : {
39380         show: function(msgEl, f){
39381             msgEl.slideIn('t', {stopFx:true});
39382         },
39383
39384         hide : function(msgEl, f){
39385             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39386         }
39387     },
39388
39389     slideRight : {
39390         show: function(msgEl, f){
39391             msgEl.fixDisplay();
39392             msgEl.alignTo(f.el, 'tl-tr');
39393             msgEl.slideIn('l', {stopFx:true});
39394         },
39395
39396         hide : function(msgEl, f){
39397             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39398         }
39399     }
39400 };/*
39401  * Based on:
39402  * Ext JS Library 1.1.1
39403  * Copyright(c) 2006-2007, Ext JS, LLC.
39404  *
39405  * Originally Released Under LGPL - original licence link has changed is not relivant.
39406  *
39407  * Fork - LGPL
39408  * <script type="text/javascript">
39409  */
39410  
39411
39412 /**
39413  * @class Roo.form.TextField
39414  * @extends Roo.form.Field
39415  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39416  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39417  * @constructor
39418  * Creates a new TextField
39419  * @param {Object} config Configuration options
39420  */
39421 Roo.form.TextField = function(config){
39422     Roo.form.TextField.superclass.constructor.call(this, config);
39423     this.addEvents({
39424         /**
39425          * @event autosize
39426          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39427          * according to the default logic, but this event provides a hook for the developer to apply additional
39428          * logic at runtime to resize the field if needed.
39429              * @param {Roo.form.Field} this This text field
39430              * @param {Number} width The new field width
39431              */
39432         autosize : true
39433     });
39434 };
39435
39436 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39437     /**
39438      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39439      */
39440     grow : false,
39441     /**
39442      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39443      */
39444     growMin : 30,
39445     /**
39446      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39447      */
39448     growMax : 800,
39449     /**
39450      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39451      */
39452     vtype : null,
39453     /**
39454      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39455      */
39456     maskRe : null,
39457     /**
39458      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39459      */
39460     disableKeyFilter : false,
39461     /**
39462      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39463      */
39464     allowBlank : true,
39465     /**
39466      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39467      */
39468     minLength : 0,
39469     /**
39470      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39471      */
39472     maxLength : Number.MAX_VALUE,
39473     /**
39474      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39475      */
39476     minLengthText : "The minimum length for this field is {0}",
39477     /**
39478      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39479      */
39480     maxLengthText : "The maximum length for this field is {0}",
39481     /**
39482      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39483      */
39484     selectOnFocus : false,
39485     /**
39486      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39487      */    
39488     allowLeadingSpace : false,
39489     /**
39490      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39491      */
39492     blankText : "This field is required",
39493     /**
39494      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39495      * If available, this function will be called only after the basic validators all return true, and will be passed the
39496      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39497      */
39498     validator : null,
39499     /**
39500      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39501      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39502      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39503      */
39504     regex : null,
39505     /**
39506      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39507      */
39508     regexText : "",
39509     /**
39510      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39511      */
39512     emptyText : null,
39513    
39514
39515     // private
39516     initEvents : function()
39517     {
39518         if (this.emptyText) {
39519             this.el.attr('placeholder', this.emptyText);
39520         }
39521         
39522         Roo.form.TextField.superclass.initEvents.call(this);
39523         if(this.validationEvent == 'keyup'){
39524             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39525             this.el.on('keyup', this.filterValidation, this);
39526         }
39527         else if(this.validationEvent !== false){
39528             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39529         }
39530         
39531         if(this.selectOnFocus){
39532             this.on("focus", this.preFocus, this);
39533         }
39534         if (!this.allowLeadingSpace) {
39535             this.on('blur', this.cleanLeadingSpace, this);
39536         }
39537         
39538         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39539             this.el.on("keypress", this.filterKeys, this);
39540         }
39541         if(this.grow){
39542             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39543             this.el.on("click", this.autoSize,  this);
39544         }
39545         if(this.el.is('input[type=password]') && Roo.isSafari){
39546             this.el.on('keydown', this.SafariOnKeyDown, this);
39547         }
39548     },
39549
39550     processValue : function(value){
39551         if(this.stripCharsRe){
39552             var newValue = value.replace(this.stripCharsRe, '');
39553             if(newValue !== value){
39554                 this.setRawValue(newValue);
39555                 return newValue;
39556             }
39557         }
39558         return value;
39559     },
39560
39561     filterValidation : function(e){
39562         if(!e.isNavKeyPress()){
39563             this.validationTask.delay(this.validationDelay);
39564         }
39565     },
39566
39567     // private
39568     onKeyUp : function(e){
39569         if(!e.isNavKeyPress()){
39570             this.autoSize();
39571         }
39572     },
39573     // private - clean the leading white space
39574     cleanLeadingSpace : function(e)
39575     {
39576         if ( this.inputType == 'file') {
39577             return;
39578         }
39579         
39580         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39581     },
39582     /**
39583      * Resets the current field value to the originally-loaded value and clears any validation messages.
39584      *  
39585      */
39586     reset : function(){
39587         Roo.form.TextField.superclass.reset.call(this);
39588        
39589     }, 
39590     // private
39591     preFocus : function(){
39592         
39593         if(this.selectOnFocus){
39594             this.el.dom.select();
39595         }
39596     },
39597
39598     
39599     // private
39600     filterKeys : function(e){
39601         var k = e.getKey();
39602         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39603             return;
39604         }
39605         var c = e.getCharCode(), cc = String.fromCharCode(c);
39606         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39607             return;
39608         }
39609         if(!this.maskRe.test(cc)){
39610             e.stopEvent();
39611         }
39612     },
39613
39614     setValue : function(v){
39615         
39616         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39617         
39618         this.autoSize();
39619     },
39620
39621     /**
39622      * Validates a value according to the field's validation rules and marks the field as invalid
39623      * if the validation fails
39624      * @param {Mixed} value The value to validate
39625      * @return {Boolean} True if the value is valid, else false
39626      */
39627     validateValue : function(value){
39628         if(value.length < 1)  { // if it's blank
39629              if(this.allowBlank){
39630                 this.clearInvalid();
39631                 return true;
39632              }else{
39633                 this.markInvalid(this.blankText);
39634                 return false;
39635              }
39636         }
39637         if(value.length < this.minLength){
39638             this.markInvalid(String.format(this.minLengthText, this.minLength));
39639             return false;
39640         }
39641         if(value.length > this.maxLength){
39642             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39643             return false;
39644         }
39645         if(this.vtype){
39646             var vt = Roo.form.VTypes;
39647             if(!vt[this.vtype](value, this)){
39648                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39649                 return false;
39650             }
39651         }
39652         if(typeof this.validator == "function"){
39653             var msg = this.validator(value);
39654             if(msg !== true){
39655                 this.markInvalid(msg);
39656                 return false;
39657             }
39658         }
39659         if(this.regex && !this.regex.test(value)){
39660             this.markInvalid(this.regexText);
39661             return false;
39662         }
39663         return true;
39664     },
39665
39666     /**
39667      * Selects text in this field
39668      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39669      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39670      */
39671     selectText : function(start, end){
39672         var v = this.getRawValue();
39673         if(v.length > 0){
39674             start = start === undefined ? 0 : start;
39675             end = end === undefined ? v.length : end;
39676             var d = this.el.dom;
39677             if(d.setSelectionRange){
39678                 d.setSelectionRange(start, end);
39679             }else if(d.createTextRange){
39680                 var range = d.createTextRange();
39681                 range.moveStart("character", start);
39682                 range.moveEnd("character", v.length-end);
39683                 range.select();
39684             }
39685         }
39686     },
39687
39688     /**
39689      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39690      * This only takes effect if grow = true, and fires the autosize event.
39691      */
39692     autoSize : function(){
39693         if(!this.grow || !this.rendered){
39694             return;
39695         }
39696         if(!this.metrics){
39697             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39698         }
39699         var el = this.el;
39700         var v = el.dom.value;
39701         var d = document.createElement('div');
39702         d.appendChild(document.createTextNode(v));
39703         v = d.innerHTML;
39704         d = null;
39705         v += "&#160;";
39706         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39707         this.el.setWidth(w);
39708         this.fireEvent("autosize", this, w);
39709     },
39710     
39711     // private
39712     SafariOnKeyDown : function(event)
39713     {
39714         // this is a workaround for a password hang bug on chrome/ webkit.
39715         
39716         var isSelectAll = false;
39717         
39718         if(this.el.dom.selectionEnd > 0){
39719             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39720         }
39721         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39722             event.preventDefault();
39723             this.setValue('');
39724             return;
39725         }
39726         
39727         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39728             
39729             event.preventDefault();
39730             // this is very hacky as keydown always get's upper case.
39731             
39732             var cc = String.fromCharCode(event.getCharCode());
39733             
39734             
39735             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39736             
39737         }
39738         
39739         
39740     }
39741 });/*
39742  * Based on:
39743  * Ext JS Library 1.1.1
39744  * Copyright(c) 2006-2007, Ext JS, LLC.
39745  *
39746  * Originally Released Under LGPL - original licence link has changed is not relivant.
39747  *
39748  * Fork - LGPL
39749  * <script type="text/javascript">
39750  */
39751  
39752 /**
39753  * @class Roo.form.Hidden
39754  * @extends Roo.form.TextField
39755  * Simple Hidden element used on forms 
39756  * 
39757  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39758  * 
39759  * @constructor
39760  * Creates a new Hidden form element.
39761  * @param {Object} config Configuration options
39762  */
39763
39764
39765
39766 // easy hidden field...
39767 Roo.form.Hidden = function(config){
39768     Roo.form.Hidden.superclass.constructor.call(this, config);
39769 };
39770   
39771 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39772     fieldLabel:      '',
39773     inputType:      'hidden',
39774     width:          50,
39775     allowBlank:     true,
39776     labelSeparator: '',
39777     hidden:         true,
39778     itemCls :       'x-form-item-display-none'
39779
39780
39781 });
39782
39783
39784 /*
39785  * Based on:
39786  * Ext JS Library 1.1.1
39787  * Copyright(c) 2006-2007, Ext JS, LLC.
39788  *
39789  * Originally Released Under LGPL - original licence link has changed is not relivant.
39790  *
39791  * Fork - LGPL
39792  * <script type="text/javascript">
39793  */
39794  
39795 /**
39796  * @class Roo.form.TriggerField
39797  * @extends Roo.form.TextField
39798  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39799  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39800  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39801  * for which you can provide a custom implementation.  For example:
39802  * <pre><code>
39803 var trigger = new Roo.form.TriggerField();
39804 trigger.onTriggerClick = myTriggerFn;
39805 trigger.applyTo('my-field');
39806 </code></pre>
39807  *
39808  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39809  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39810  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39811  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39812  * @constructor
39813  * Create a new TriggerField.
39814  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39815  * to the base TextField)
39816  */
39817 Roo.form.TriggerField = function(config){
39818     this.mimicing = false;
39819     Roo.form.TriggerField.superclass.constructor.call(this, config);
39820 };
39821
39822 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39823     /**
39824      * @cfg {String} triggerClass A CSS class to apply to the trigger
39825      */
39826     /**
39827      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39828      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39829      */
39830     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39831     /**
39832      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39833      */
39834     hideTrigger:false,
39835
39836     /** @cfg {Boolean} grow @hide */
39837     /** @cfg {Number} growMin @hide */
39838     /** @cfg {Number} growMax @hide */
39839
39840     /**
39841      * @hide 
39842      * @method
39843      */
39844     autoSize: Roo.emptyFn,
39845     // private
39846     monitorTab : true,
39847     // private
39848     deferHeight : true,
39849
39850     
39851     actionMode : 'wrap',
39852     // private
39853     onResize : function(w, h){
39854         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39855         if(typeof w == 'number'){
39856             var x = w - this.trigger.getWidth();
39857             this.el.setWidth(this.adjustWidth('input', x));
39858             this.trigger.setStyle('left', x+'px');
39859         }
39860     },
39861
39862     // private
39863     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39864
39865     // private
39866     getResizeEl : function(){
39867         return this.wrap;
39868     },
39869
39870     // private
39871     getPositionEl : function(){
39872         return this.wrap;
39873     },
39874
39875     // private
39876     alignErrorIcon : function(){
39877         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39878     },
39879
39880     // private
39881     onRender : function(ct, position){
39882         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39883         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39884         this.trigger = this.wrap.createChild(this.triggerConfig ||
39885                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39886         if(this.hideTrigger){
39887             this.trigger.setDisplayed(false);
39888         }
39889         this.initTrigger();
39890         if(!this.width){
39891             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39892         }
39893     },
39894
39895     // private
39896     initTrigger : function(){
39897         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39898         this.trigger.addClassOnOver('x-form-trigger-over');
39899         this.trigger.addClassOnClick('x-form-trigger-click');
39900     },
39901
39902     // private
39903     onDestroy : function(){
39904         if(this.trigger){
39905             this.trigger.removeAllListeners();
39906             this.trigger.remove();
39907         }
39908         if(this.wrap){
39909             this.wrap.remove();
39910         }
39911         Roo.form.TriggerField.superclass.onDestroy.call(this);
39912     },
39913
39914     // private
39915     onFocus : function(){
39916         Roo.form.TriggerField.superclass.onFocus.call(this);
39917         if(!this.mimicing){
39918             this.wrap.addClass('x-trigger-wrap-focus');
39919             this.mimicing = true;
39920             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39921             if(this.monitorTab){
39922                 this.el.on("keydown", this.checkTab, this);
39923             }
39924         }
39925     },
39926
39927     // private
39928     checkTab : function(e){
39929         if(e.getKey() == e.TAB){
39930             this.triggerBlur();
39931         }
39932     },
39933
39934     // private
39935     onBlur : function(){
39936         // do nothing
39937     },
39938
39939     // private
39940     mimicBlur : function(e, t){
39941         if(!this.wrap.contains(t) && this.validateBlur()){
39942             this.triggerBlur();
39943         }
39944     },
39945
39946     // private
39947     triggerBlur : function(){
39948         this.mimicing = false;
39949         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39950         if(this.monitorTab){
39951             this.el.un("keydown", this.checkTab, this);
39952         }
39953         this.wrap.removeClass('x-trigger-wrap-focus');
39954         Roo.form.TriggerField.superclass.onBlur.call(this);
39955     },
39956
39957     // private
39958     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39959     validateBlur : function(e, t){
39960         return true;
39961     },
39962
39963     // private
39964     onDisable : function(){
39965         Roo.form.TriggerField.superclass.onDisable.call(this);
39966         if(this.wrap){
39967             this.wrap.addClass('x-item-disabled');
39968         }
39969     },
39970
39971     // private
39972     onEnable : function(){
39973         Roo.form.TriggerField.superclass.onEnable.call(this);
39974         if(this.wrap){
39975             this.wrap.removeClass('x-item-disabled');
39976         }
39977     },
39978
39979     // private
39980     onShow : function(){
39981         var ae = this.getActionEl();
39982         
39983         if(ae){
39984             ae.dom.style.display = '';
39985             ae.dom.style.visibility = 'visible';
39986         }
39987     },
39988
39989     // private
39990     
39991     onHide : function(){
39992         var ae = this.getActionEl();
39993         ae.dom.style.display = 'none';
39994     },
39995
39996     /**
39997      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39998      * by an implementing function.
39999      * @method
40000      * @param {EventObject} e
40001      */
40002     onTriggerClick : Roo.emptyFn
40003 });
40004
40005 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40006 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40007 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40008 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40009     initComponent : function(){
40010         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40011
40012         this.triggerConfig = {
40013             tag:'span', cls:'x-form-twin-triggers', cn:[
40014             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40015             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40016         ]};
40017     },
40018
40019     getTrigger : function(index){
40020         return this.triggers[index];
40021     },
40022
40023     initTrigger : function(){
40024         var ts = this.trigger.select('.x-form-trigger', true);
40025         this.wrap.setStyle('overflow', 'hidden');
40026         var triggerField = this;
40027         ts.each(function(t, all, index){
40028             t.hide = function(){
40029                 var w = triggerField.wrap.getWidth();
40030                 this.dom.style.display = 'none';
40031                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40032             };
40033             t.show = function(){
40034                 var w = triggerField.wrap.getWidth();
40035                 this.dom.style.display = '';
40036                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40037             };
40038             var triggerIndex = 'Trigger'+(index+1);
40039
40040             if(this['hide'+triggerIndex]){
40041                 t.dom.style.display = 'none';
40042             }
40043             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40044             t.addClassOnOver('x-form-trigger-over');
40045             t.addClassOnClick('x-form-trigger-click');
40046         }, this);
40047         this.triggers = ts.elements;
40048     },
40049
40050     onTrigger1Click : Roo.emptyFn,
40051     onTrigger2Click : Roo.emptyFn
40052 });/*
40053  * Based on:
40054  * Ext JS Library 1.1.1
40055  * Copyright(c) 2006-2007, Ext JS, LLC.
40056  *
40057  * Originally Released Under LGPL - original licence link has changed is not relivant.
40058  *
40059  * Fork - LGPL
40060  * <script type="text/javascript">
40061  */
40062  
40063 /**
40064  * @class Roo.form.TextArea
40065  * @extends Roo.form.TextField
40066  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40067  * support for auto-sizing.
40068  * @constructor
40069  * Creates a new TextArea
40070  * @param {Object} config Configuration options
40071  */
40072 Roo.form.TextArea = function(config){
40073     Roo.form.TextArea.superclass.constructor.call(this, config);
40074     // these are provided exchanges for backwards compat
40075     // minHeight/maxHeight were replaced by growMin/growMax to be
40076     // compatible with TextField growing config values
40077     if(this.minHeight !== undefined){
40078         this.growMin = this.minHeight;
40079     }
40080     if(this.maxHeight !== undefined){
40081         this.growMax = this.maxHeight;
40082     }
40083 };
40084
40085 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40086     /**
40087      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40088      */
40089     growMin : 60,
40090     /**
40091      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40092      */
40093     growMax: 1000,
40094     /**
40095      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40096      * in the field (equivalent to setting overflow: hidden, defaults to false)
40097      */
40098     preventScrollbars: false,
40099     /**
40100      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40101      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40102      */
40103
40104     // private
40105     onRender : function(ct, position){
40106         if(!this.el){
40107             this.defaultAutoCreate = {
40108                 tag: "textarea",
40109                 style:"width:300px;height:60px;",
40110                 autocomplete: "new-password"
40111             };
40112         }
40113         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40114         if(this.grow){
40115             this.textSizeEl = Roo.DomHelper.append(document.body, {
40116                 tag: "pre", cls: "x-form-grow-sizer"
40117             });
40118             if(this.preventScrollbars){
40119                 this.el.setStyle("overflow", "hidden");
40120             }
40121             this.el.setHeight(this.growMin);
40122         }
40123     },
40124
40125     onDestroy : function(){
40126         if(this.textSizeEl){
40127             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40128         }
40129         Roo.form.TextArea.superclass.onDestroy.call(this);
40130     },
40131
40132     // private
40133     onKeyUp : function(e){
40134         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40135             this.autoSize();
40136         }
40137     },
40138
40139     /**
40140      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40141      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40142      */
40143     autoSize : function(){
40144         if(!this.grow || !this.textSizeEl){
40145             return;
40146         }
40147         var el = this.el;
40148         var v = el.dom.value;
40149         var ts = this.textSizeEl;
40150
40151         ts.innerHTML = '';
40152         ts.appendChild(document.createTextNode(v));
40153         v = ts.innerHTML;
40154
40155         Roo.fly(ts).setWidth(this.el.getWidth());
40156         if(v.length < 1){
40157             v = "&#160;&#160;";
40158         }else{
40159             if(Roo.isIE){
40160                 v = v.replace(/\n/g, '<p>&#160;</p>');
40161             }
40162             v += "&#160;\n&#160;";
40163         }
40164         ts.innerHTML = v;
40165         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40166         if(h != this.lastHeight){
40167             this.lastHeight = h;
40168             this.el.setHeight(h);
40169             this.fireEvent("autosize", this, h);
40170         }
40171     }
40172 });/*
40173  * Based on:
40174  * Ext JS Library 1.1.1
40175  * Copyright(c) 2006-2007, Ext JS, LLC.
40176  *
40177  * Originally Released Under LGPL - original licence link has changed is not relivant.
40178  *
40179  * Fork - LGPL
40180  * <script type="text/javascript">
40181  */
40182  
40183
40184 /**
40185  * @class Roo.form.NumberField
40186  * @extends Roo.form.TextField
40187  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40188  * @constructor
40189  * Creates a new NumberField
40190  * @param {Object} config Configuration options
40191  */
40192 Roo.form.NumberField = function(config){
40193     Roo.form.NumberField.superclass.constructor.call(this, config);
40194 };
40195
40196 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40197     /**
40198      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40199      */
40200     fieldClass: "x-form-field x-form-num-field",
40201     /**
40202      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40203      */
40204     allowDecimals : true,
40205     /**
40206      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40207      */
40208     decimalSeparator : ".",
40209     /**
40210      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40211      */
40212     decimalPrecision : 2,
40213     /**
40214      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40215      */
40216     allowNegative : true,
40217     /**
40218      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40219      */
40220     minValue : Number.NEGATIVE_INFINITY,
40221     /**
40222      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40223      */
40224     maxValue : Number.MAX_VALUE,
40225     /**
40226      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40227      */
40228     minText : "The minimum value for this field is {0}",
40229     /**
40230      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40231      */
40232     maxText : "The maximum value for this field is {0}",
40233     /**
40234      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40235      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40236      */
40237     nanText : "{0} is not a valid number",
40238
40239     // private
40240     initEvents : function(){
40241         Roo.form.NumberField.superclass.initEvents.call(this);
40242         var allowed = "0123456789";
40243         if(this.allowDecimals){
40244             allowed += this.decimalSeparator;
40245         }
40246         if(this.allowNegative){
40247             allowed += "-";
40248         }
40249         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40250         var keyPress = function(e){
40251             var k = e.getKey();
40252             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40253                 return;
40254             }
40255             var c = e.getCharCode();
40256             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40257                 e.stopEvent();
40258             }
40259         };
40260         this.el.on("keypress", keyPress, this);
40261     },
40262
40263     // private
40264     validateValue : function(value){
40265         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40266             return false;
40267         }
40268         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40269              return true;
40270         }
40271         var num = this.parseValue(value);
40272         if(isNaN(num)){
40273             this.markInvalid(String.format(this.nanText, value));
40274             return false;
40275         }
40276         if(num < this.minValue){
40277             this.markInvalid(String.format(this.minText, this.minValue));
40278             return false;
40279         }
40280         if(num > this.maxValue){
40281             this.markInvalid(String.format(this.maxText, this.maxValue));
40282             return false;
40283         }
40284         return true;
40285     },
40286
40287     getValue : function(){
40288         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40289     },
40290
40291     // private
40292     parseValue : function(value){
40293         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40294         return isNaN(value) ? '' : value;
40295     },
40296
40297     // private
40298     fixPrecision : function(value){
40299         var nan = isNaN(value);
40300         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40301             return nan ? '' : value;
40302         }
40303         return parseFloat(value).toFixed(this.decimalPrecision);
40304     },
40305
40306     setValue : function(v){
40307         v = this.fixPrecision(v);
40308         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40309     },
40310
40311     // private
40312     decimalPrecisionFcn : function(v){
40313         return Math.floor(v);
40314     },
40315
40316     beforeBlur : function(){
40317         var v = this.parseValue(this.getRawValue());
40318         if(v){
40319             this.setValue(v);
40320         }
40321     }
40322 });/*
40323  * Based on:
40324  * Ext JS Library 1.1.1
40325  * Copyright(c) 2006-2007, Ext JS, LLC.
40326  *
40327  * Originally Released Under LGPL - original licence link has changed is not relivant.
40328  *
40329  * Fork - LGPL
40330  * <script type="text/javascript">
40331  */
40332  
40333 /**
40334  * @class Roo.form.DateField
40335  * @extends Roo.form.TriggerField
40336  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40337 * @constructor
40338 * Create a new DateField
40339 * @param {Object} config
40340  */
40341 Roo.form.DateField = function(config)
40342 {
40343     Roo.form.DateField.superclass.constructor.call(this, config);
40344     
40345       this.addEvents({
40346          
40347         /**
40348          * @event select
40349          * Fires when a date is selected
40350              * @param {Roo.form.DateField} combo This combo box
40351              * @param {Date} date The date selected
40352              */
40353         'select' : true
40354          
40355     });
40356     
40357     
40358     if(typeof this.minValue == "string") {
40359         this.minValue = this.parseDate(this.minValue);
40360     }
40361     if(typeof this.maxValue == "string") {
40362         this.maxValue = this.parseDate(this.maxValue);
40363     }
40364     this.ddMatch = null;
40365     if(this.disabledDates){
40366         var dd = this.disabledDates;
40367         var re = "(?:";
40368         for(var i = 0; i < dd.length; i++){
40369             re += dd[i];
40370             if(i != dd.length-1) {
40371                 re += "|";
40372             }
40373         }
40374         this.ddMatch = new RegExp(re + ")");
40375     }
40376 };
40377
40378 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40379     /**
40380      * @cfg {String} format
40381      * The default date format string which can be overriden for localization support.  The format must be
40382      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40383      */
40384     format : "m/d/y",
40385     /**
40386      * @cfg {String} altFormats
40387      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40388      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40389      */
40390     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40391     /**
40392      * @cfg {Array} disabledDays
40393      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40394      */
40395     disabledDays : null,
40396     /**
40397      * @cfg {String} disabledDaysText
40398      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40399      */
40400     disabledDaysText : "Disabled",
40401     /**
40402      * @cfg {Array} disabledDates
40403      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40404      * expression so they are very powerful. Some examples:
40405      * <ul>
40406      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40407      * <li>["03/08", "09/16"] would disable those days for every year</li>
40408      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40409      * <li>["03/../2006"] would disable every day in March 2006</li>
40410      * <li>["^03"] would disable every day in every March</li>
40411      * </ul>
40412      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40413      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40414      */
40415     disabledDates : null,
40416     /**
40417      * @cfg {String} disabledDatesText
40418      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40419      */
40420     disabledDatesText : "Disabled",
40421     /**
40422      * @cfg {Date/String} minValue
40423      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40424      * valid format (defaults to null).
40425      */
40426     minValue : null,
40427     /**
40428      * @cfg {Date/String} maxValue
40429      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40430      * valid format (defaults to null).
40431      */
40432     maxValue : null,
40433     /**
40434      * @cfg {String} minText
40435      * The error text to display when the date in the cell is before minValue (defaults to
40436      * 'The date in this field must be after {minValue}').
40437      */
40438     minText : "The date in this field must be equal to or after {0}",
40439     /**
40440      * @cfg {String} maxText
40441      * The error text to display when the date in the cell is after maxValue (defaults to
40442      * 'The date in this field must be before {maxValue}').
40443      */
40444     maxText : "The date in this field must be equal to or before {0}",
40445     /**
40446      * @cfg {String} invalidText
40447      * The error text to display when the date in the field is invalid (defaults to
40448      * '{value} is not a valid date - it must be in the format {format}').
40449      */
40450     invalidText : "{0} is not a valid date - it must be in the format {1}",
40451     /**
40452      * @cfg {String} triggerClass
40453      * An additional CSS class used to style the trigger button.  The trigger will always get the
40454      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40455      * which displays a calendar icon).
40456      */
40457     triggerClass : 'x-form-date-trigger',
40458     
40459
40460     /**
40461      * @cfg {Boolean} useIso
40462      * if enabled, then the date field will use a hidden field to store the 
40463      * real value as iso formated date. default (false)
40464      */ 
40465     useIso : false,
40466     /**
40467      * @cfg {String/Object} autoCreate
40468      * A DomHelper element spec, or true for a default element spec (defaults to
40469      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40470      */ 
40471     // private
40472     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40473     
40474     // private
40475     hiddenField: false,
40476     
40477     onRender : function(ct, position)
40478     {
40479         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40480         if (this.useIso) {
40481             //this.el.dom.removeAttribute('name'); 
40482             Roo.log("Changing name?");
40483             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40484             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40485                     'before', true);
40486             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40487             // prevent input submission
40488             this.hiddenName = this.name;
40489         }
40490             
40491             
40492     },
40493     
40494     // private
40495     validateValue : function(value)
40496     {
40497         value = this.formatDate(value);
40498         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40499             Roo.log('super failed');
40500             return false;
40501         }
40502         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40503              return true;
40504         }
40505         var svalue = value;
40506         value = this.parseDate(value);
40507         if(!value){
40508             Roo.log('parse date failed' + svalue);
40509             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40510             return false;
40511         }
40512         var time = value.getTime();
40513         if(this.minValue && time < this.minValue.getTime()){
40514             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40515             return false;
40516         }
40517         if(this.maxValue && time > this.maxValue.getTime()){
40518             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40519             return false;
40520         }
40521         if(this.disabledDays){
40522             var day = value.getDay();
40523             for(var i = 0; i < this.disabledDays.length; i++) {
40524                 if(day === this.disabledDays[i]){
40525                     this.markInvalid(this.disabledDaysText);
40526                     return false;
40527                 }
40528             }
40529         }
40530         var fvalue = this.formatDate(value);
40531         if(this.ddMatch && this.ddMatch.test(fvalue)){
40532             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40533             return false;
40534         }
40535         return true;
40536     },
40537
40538     // private
40539     // Provides logic to override the default TriggerField.validateBlur which just returns true
40540     validateBlur : function(){
40541         return !this.menu || !this.menu.isVisible();
40542     },
40543     
40544     getName: function()
40545     {
40546         // returns hidden if it's set..
40547         if (!this.rendered) {return ''};
40548         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40549         
40550     },
40551
40552     /**
40553      * Returns the current date value of the date field.
40554      * @return {Date} The date value
40555      */
40556     getValue : function(){
40557         
40558         return  this.hiddenField ?
40559                 this.hiddenField.value :
40560                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40561     },
40562
40563     /**
40564      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40565      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40566      * (the default format used is "m/d/y").
40567      * <br />Usage:
40568      * <pre><code>
40569 //All of these calls set the same date value (May 4, 2006)
40570
40571 //Pass a date object:
40572 var dt = new Date('5/4/06');
40573 dateField.setValue(dt);
40574
40575 //Pass a date string (default format):
40576 dateField.setValue('5/4/06');
40577
40578 //Pass a date string (custom format):
40579 dateField.format = 'Y-m-d';
40580 dateField.setValue('2006-5-4');
40581 </code></pre>
40582      * @param {String/Date} date The date or valid date string
40583      */
40584     setValue : function(date){
40585         if (this.hiddenField) {
40586             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40587         }
40588         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40589         // make sure the value field is always stored as a date..
40590         this.value = this.parseDate(date);
40591         
40592         
40593     },
40594
40595     // private
40596     parseDate : function(value){
40597         if(!value || value instanceof Date){
40598             return value;
40599         }
40600         var v = Date.parseDate(value, this.format);
40601          if (!v && this.useIso) {
40602             v = Date.parseDate(value, 'Y-m-d');
40603         }
40604         if(!v && this.altFormats){
40605             if(!this.altFormatsArray){
40606                 this.altFormatsArray = this.altFormats.split("|");
40607             }
40608             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40609                 v = Date.parseDate(value, this.altFormatsArray[i]);
40610             }
40611         }
40612         return v;
40613     },
40614
40615     // private
40616     formatDate : function(date, fmt){
40617         return (!date || !(date instanceof Date)) ?
40618                date : date.dateFormat(fmt || this.format);
40619     },
40620
40621     // private
40622     menuListeners : {
40623         select: function(m, d){
40624             
40625             this.setValue(d);
40626             this.fireEvent('select', this, d);
40627         },
40628         show : function(){ // retain focus styling
40629             this.onFocus();
40630         },
40631         hide : function(){
40632             this.focus.defer(10, this);
40633             var ml = this.menuListeners;
40634             this.menu.un("select", ml.select,  this);
40635             this.menu.un("show", ml.show,  this);
40636             this.menu.un("hide", ml.hide,  this);
40637         }
40638     },
40639
40640     // private
40641     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40642     onTriggerClick : function(){
40643         if(this.disabled){
40644             return;
40645         }
40646         if(this.menu == null){
40647             this.menu = new Roo.menu.DateMenu();
40648         }
40649         Roo.apply(this.menu.picker,  {
40650             showClear: this.allowBlank,
40651             minDate : this.minValue,
40652             maxDate : this.maxValue,
40653             disabledDatesRE : this.ddMatch,
40654             disabledDatesText : this.disabledDatesText,
40655             disabledDays : this.disabledDays,
40656             disabledDaysText : this.disabledDaysText,
40657             format : this.useIso ? 'Y-m-d' : this.format,
40658             minText : String.format(this.minText, this.formatDate(this.minValue)),
40659             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40660         });
40661         this.menu.on(Roo.apply({}, this.menuListeners, {
40662             scope:this
40663         }));
40664         this.menu.picker.setValue(this.getValue() || new Date());
40665         this.menu.show(this.el, "tl-bl?");
40666     },
40667
40668     beforeBlur : function(){
40669         var v = this.parseDate(this.getRawValue());
40670         if(v){
40671             this.setValue(v);
40672         }
40673     },
40674
40675     /*@
40676      * overide
40677      * 
40678      */
40679     isDirty : function() {
40680         if(this.disabled) {
40681             return false;
40682         }
40683         
40684         if(typeof(this.startValue) === 'undefined'){
40685             return false;
40686         }
40687         
40688         return String(this.getValue()) !== String(this.startValue);
40689         
40690     },
40691     // @overide
40692     cleanLeadingSpace : function(e)
40693     {
40694        return;
40695     }
40696     
40697 });/*
40698  * Based on:
40699  * Ext JS Library 1.1.1
40700  * Copyright(c) 2006-2007, Ext JS, LLC.
40701  *
40702  * Originally Released Under LGPL - original licence link has changed is not relivant.
40703  *
40704  * Fork - LGPL
40705  * <script type="text/javascript">
40706  */
40707  
40708 /**
40709  * @class Roo.form.MonthField
40710  * @extends Roo.form.TriggerField
40711  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40712 * @constructor
40713 * Create a new MonthField
40714 * @param {Object} config
40715  */
40716 Roo.form.MonthField = function(config){
40717     
40718     Roo.form.MonthField.superclass.constructor.call(this, config);
40719     
40720       this.addEvents({
40721          
40722         /**
40723          * @event select
40724          * Fires when a date is selected
40725              * @param {Roo.form.MonthFieeld} combo This combo box
40726              * @param {Date} date The date selected
40727              */
40728         'select' : true
40729          
40730     });
40731     
40732     
40733     if(typeof this.minValue == "string") {
40734         this.minValue = this.parseDate(this.minValue);
40735     }
40736     if(typeof this.maxValue == "string") {
40737         this.maxValue = this.parseDate(this.maxValue);
40738     }
40739     this.ddMatch = null;
40740     if(this.disabledDates){
40741         var dd = this.disabledDates;
40742         var re = "(?:";
40743         for(var i = 0; i < dd.length; i++){
40744             re += dd[i];
40745             if(i != dd.length-1) {
40746                 re += "|";
40747             }
40748         }
40749         this.ddMatch = new RegExp(re + ")");
40750     }
40751 };
40752
40753 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40754     /**
40755      * @cfg {String} format
40756      * The default date format string which can be overriden for localization support.  The format must be
40757      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40758      */
40759     format : "M Y",
40760     /**
40761      * @cfg {String} altFormats
40762      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40763      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40764      */
40765     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40766     /**
40767      * @cfg {Array} disabledDays
40768      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40769      */
40770     disabledDays : [0,1,2,3,4,5,6],
40771     /**
40772      * @cfg {String} disabledDaysText
40773      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40774      */
40775     disabledDaysText : "Disabled",
40776     /**
40777      * @cfg {Array} disabledDates
40778      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40779      * expression so they are very powerful. Some examples:
40780      * <ul>
40781      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40782      * <li>["03/08", "09/16"] would disable those days for every year</li>
40783      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40784      * <li>["03/../2006"] would disable every day in March 2006</li>
40785      * <li>["^03"] would disable every day in every March</li>
40786      * </ul>
40787      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40788      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40789      */
40790     disabledDates : null,
40791     /**
40792      * @cfg {String} disabledDatesText
40793      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40794      */
40795     disabledDatesText : "Disabled",
40796     /**
40797      * @cfg {Date/String} minValue
40798      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40799      * valid format (defaults to null).
40800      */
40801     minValue : null,
40802     /**
40803      * @cfg {Date/String} maxValue
40804      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40805      * valid format (defaults to null).
40806      */
40807     maxValue : null,
40808     /**
40809      * @cfg {String} minText
40810      * The error text to display when the date in the cell is before minValue (defaults to
40811      * 'The date in this field must be after {minValue}').
40812      */
40813     minText : "The date in this field must be equal to or after {0}",
40814     /**
40815      * @cfg {String} maxTextf
40816      * The error text to display when the date in the cell is after maxValue (defaults to
40817      * 'The date in this field must be before {maxValue}').
40818      */
40819     maxText : "The date in this field must be equal to or before {0}",
40820     /**
40821      * @cfg {String} invalidText
40822      * The error text to display when the date in the field is invalid (defaults to
40823      * '{value} is not a valid date - it must be in the format {format}').
40824      */
40825     invalidText : "{0} is not a valid date - it must be in the format {1}",
40826     /**
40827      * @cfg {String} triggerClass
40828      * An additional CSS class used to style the trigger button.  The trigger will always get the
40829      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40830      * which displays a calendar icon).
40831      */
40832     triggerClass : 'x-form-date-trigger',
40833     
40834
40835     /**
40836      * @cfg {Boolean} useIso
40837      * if enabled, then the date field will use a hidden field to store the 
40838      * real value as iso formated date. default (true)
40839      */ 
40840     useIso : true,
40841     /**
40842      * @cfg {String/Object} autoCreate
40843      * A DomHelper element spec, or true for a default element spec (defaults to
40844      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40845      */ 
40846     // private
40847     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40848     
40849     // private
40850     hiddenField: false,
40851     
40852     hideMonthPicker : false,
40853     
40854     onRender : function(ct, position)
40855     {
40856         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40857         if (this.useIso) {
40858             this.el.dom.removeAttribute('name'); 
40859             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40860                     'before', true);
40861             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40862             // prevent input submission
40863             this.hiddenName = this.name;
40864         }
40865             
40866             
40867     },
40868     
40869     // private
40870     validateValue : function(value)
40871     {
40872         value = this.formatDate(value);
40873         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40874             return false;
40875         }
40876         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40877              return true;
40878         }
40879         var svalue = value;
40880         value = this.parseDate(value);
40881         if(!value){
40882             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40883             return false;
40884         }
40885         var time = value.getTime();
40886         if(this.minValue && time < this.minValue.getTime()){
40887             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40888             return false;
40889         }
40890         if(this.maxValue && time > this.maxValue.getTime()){
40891             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40892             return false;
40893         }
40894         /*if(this.disabledDays){
40895             var day = value.getDay();
40896             for(var i = 0; i < this.disabledDays.length; i++) {
40897                 if(day === this.disabledDays[i]){
40898                     this.markInvalid(this.disabledDaysText);
40899                     return false;
40900                 }
40901             }
40902         }
40903         */
40904         var fvalue = this.formatDate(value);
40905         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40906             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40907             return false;
40908         }
40909         */
40910         return true;
40911     },
40912
40913     // private
40914     // Provides logic to override the default TriggerField.validateBlur which just returns true
40915     validateBlur : function(){
40916         return !this.menu || !this.menu.isVisible();
40917     },
40918
40919     /**
40920      * Returns the current date value of the date field.
40921      * @return {Date} The date value
40922      */
40923     getValue : function(){
40924         
40925         
40926         
40927         return  this.hiddenField ?
40928                 this.hiddenField.value :
40929                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40930     },
40931
40932     /**
40933      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40934      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40935      * (the default format used is "m/d/y").
40936      * <br />Usage:
40937      * <pre><code>
40938 //All of these calls set the same date value (May 4, 2006)
40939
40940 //Pass a date object:
40941 var dt = new Date('5/4/06');
40942 monthField.setValue(dt);
40943
40944 //Pass a date string (default format):
40945 monthField.setValue('5/4/06');
40946
40947 //Pass a date string (custom format):
40948 monthField.format = 'Y-m-d';
40949 monthField.setValue('2006-5-4');
40950 </code></pre>
40951      * @param {String/Date} date The date or valid date string
40952      */
40953     setValue : function(date){
40954         Roo.log('month setValue' + date);
40955         // can only be first of month..
40956         
40957         var val = this.parseDate(date);
40958         
40959         if (this.hiddenField) {
40960             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40961         }
40962         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40963         this.value = this.parseDate(date);
40964     },
40965
40966     // private
40967     parseDate : function(value){
40968         if(!value || value instanceof Date){
40969             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40970             return value;
40971         }
40972         var v = Date.parseDate(value, this.format);
40973         if (!v && this.useIso) {
40974             v = Date.parseDate(value, 'Y-m-d');
40975         }
40976         if (v) {
40977             // 
40978             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40979         }
40980         
40981         
40982         if(!v && this.altFormats){
40983             if(!this.altFormatsArray){
40984                 this.altFormatsArray = this.altFormats.split("|");
40985             }
40986             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40987                 v = Date.parseDate(value, this.altFormatsArray[i]);
40988             }
40989         }
40990         return v;
40991     },
40992
40993     // private
40994     formatDate : function(date, fmt){
40995         return (!date || !(date instanceof Date)) ?
40996                date : date.dateFormat(fmt || this.format);
40997     },
40998
40999     // private
41000     menuListeners : {
41001         select: function(m, d){
41002             this.setValue(d);
41003             this.fireEvent('select', this, d);
41004         },
41005         show : function(){ // retain focus styling
41006             this.onFocus();
41007         },
41008         hide : function(){
41009             this.focus.defer(10, this);
41010             var ml = this.menuListeners;
41011             this.menu.un("select", ml.select,  this);
41012             this.menu.un("show", ml.show,  this);
41013             this.menu.un("hide", ml.hide,  this);
41014         }
41015     },
41016     // private
41017     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41018     onTriggerClick : function(){
41019         if(this.disabled){
41020             return;
41021         }
41022         if(this.menu == null){
41023             this.menu = new Roo.menu.DateMenu();
41024            
41025         }
41026         
41027         Roo.apply(this.menu.picker,  {
41028             
41029             showClear: this.allowBlank,
41030             minDate : this.minValue,
41031             maxDate : this.maxValue,
41032             disabledDatesRE : this.ddMatch,
41033             disabledDatesText : this.disabledDatesText,
41034             
41035             format : this.useIso ? 'Y-m-d' : this.format,
41036             minText : String.format(this.minText, this.formatDate(this.minValue)),
41037             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41038             
41039         });
41040          this.menu.on(Roo.apply({}, this.menuListeners, {
41041             scope:this
41042         }));
41043        
41044         
41045         var m = this.menu;
41046         var p = m.picker;
41047         
41048         // hide month picker get's called when we called by 'before hide';
41049         
41050         var ignorehide = true;
41051         p.hideMonthPicker  = function(disableAnim){
41052             if (ignorehide) {
41053                 return;
41054             }
41055              if(this.monthPicker){
41056                 Roo.log("hideMonthPicker called");
41057                 if(disableAnim === true){
41058                     this.monthPicker.hide();
41059                 }else{
41060                     this.monthPicker.slideOut('t', {duration:.2});
41061                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41062                     p.fireEvent("select", this, this.value);
41063                     m.hide();
41064                 }
41065             }
41066         }
41067         
41068         Roo.log('picker set value');
41069         Roo.log(this.getValue());
41070         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41071         m.show(this.el, 'tl-bl?');
41072         ignorehide  = false;
41073         // this will trigger hideMonthPicker..
41074         
41075         
41076         // hidden the day picker
41077         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41078         
41079         
41080         
41081       
41082         
41083         p.showMonthPicker.defer(100, p);
41084     
41085         
41086        
41087     },
41088
41089     beforeBlur : function(){
41090         var v = this.parseDate(this.getRawValue());
41091         if(v){
41092             this.setValue(v);
41093         }
41094     }
41095
41096     /** @cfg {Boolean} grow @hide */
41097     /** @cfg {Number} growMin @hide */
41098     /** @cfg {Number} growMax @hide */
41099     /**
41100      * @hide
41101      * @method autoSize
41102      */
41103 });/*
41104  * Based on:
41105  * Ext JS Library 1.1.1
41106  * Copyright(c) 2006-2007, Ext JS, LLC.
41107  *
41108  * Originally Released Under LGPL - original licence link has changed is not relivant.
41109  *
41110  * Fork - LGPL
41111  * <script type="text/javascript">
41112  */
41113  
41114
41115 /**
41116  * @class Roo.form.ComboBox
41117  * @extends Roo.form.TriggerField
41118  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41119  * @constructor
41120  * Create a new ComboBox.
41121  * @param {Object} config Configuration options
41122  */
41123 Roo.form.ComboBox = function(config){
41124     Roo.form.ComboBox.superclass.constructor.call(this, config);
41125     this.addEvents({
41126         /**
41127          * @event expand
41128          * Fires when the dropdown list is expanded
41129              * @param {Roo.form.ComboBox} combo This combo box
41130              */
41131         'expand' : true,
41132         /**
41133          * @event collapse
41134          * Fires when the dropdown list is collapsed
41135              * @param {Roo.form.ComboBox} combo This combo box
41136              */
41137         'collapse' : true,
41138         /**
41139          * @event beforeselect
41140          * Fires before a list item is selected. Return false to cancel the selection.
41141              * @param {Roo.form.ComboBox} combo This combo box
41142              * @param {Roo.data.Record} record The data record returned from the underlying store
41143              * @param {Number} index The index of the selected item in the dropdown list
41144              */
41145         'beforeselect' : true,
41146         /**
41147          * @event select
41148          * Fires when a list item is selected
41149              * @param {Roo.form.ComboBox} combo This combo box
41150              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41151              * @param {Number} index The index of the selected item in the dropdown list
41152              */
41153         'select' : true,
41154         /**
41155          * @event beforequery
41156          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41157          * The event object passed has these properties:
41158              * @param {Roo.form.ComboBox} combo This combo box
41159              * @param {String} query The query
41160              * @param {Boolean} forceAll true to force "all" query
41161              * @param {Boolean} cancel true to cancel the query
41162              * @param {Object} e The query event object
41163              */
41164         'beforequery': true,
41165          /**
41166          * @event add
41167          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41168              * @param {Roo.form.ComboBox} combo This combo box
41169              */
41170         'add' : true,
41171         /**
41172          * @event edit
41173          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41174              * @param {Roo.form.ComboBox} combo This combo box
41175              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41176              */
41177         'edit' : true
41178         
41179         
41180     });
41181     if(this.transform){
41182         this.allowDomMove = false;
41183         var s = Roo.getDom(this.transform);
41184         if(!this.hiddenName){
41185             this.hiddenName = s.name;
41186         }
41187         if(!this.store){
41188             this.mode = 'local';
41189             var d = [], opts = s.options;
41190             for(var i = 0, len = opts.length;i < len; i++){
41191                 var o = opts[i];
41192                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41193                 if(o.selected) {
41194                     this.value = value;
41195                 }
41196                 d.push([value, o.text]);
41197             }
41198             this.store = new Roo.data.SimpleStore({
41199                 'id': 0,
41200                 fields: ['value', 'text'],
41201                 data : d
41202             });
41203             this.valueField = 'value';
41204             this.displayField = 'text';
41205         }
41206         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41207         if(!this.lazyRender){
41208             this.target = true;
41209             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41210             s.parentNode.removeChild(s); // remove it
41211             this.render(this.el.parentNode);
41212         }else{
41213             s.parentNode.removeChild(s); // remove it
41214         }
41215
41216     }
41217     if (this.store) {
41218         this.store = Roo.factory(this.store, Roo.data);
41219     }
41220     
41221     this.selectedIndex = -1;
41222     if(this.mode == 'local'){
41223         if(config.queryDelay === undefined){
41224             this.queryDelay = 10;
41225         }
41226         if(config.minChars === undefined){
41227             this.minChars = 0;
41228         }
41229     }
41230 };
41231
41232 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41233     /**
41234      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41235      */
41236     /**
41237      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41238      * rendering into an Roo.Editor, defaults to false)
41239      */
41240     /**
41241      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41242      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41243      */
41244     /**
41245      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41246      */
41247     /**
41248      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41249      * the dropdown list (defaults to undefined, with no header element)
41250      */
41251
41252      /**
41253      * @cfg {String/Roo.Template} tpl The template to use to render the output
41254      */
41255      
41256     // private
41257     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41258     /**
41259      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41260      */
41261     listWidth: undefined,
41262     /**
41263      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41264      * mode = 'remote' or 'text' if mode = 'local')
41265      */
41266     displayField: undefined,
41267     /**
41268      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41269      * mode = 'remote' or 'value' if mode = 'local'). 
41270      * Note: use of a valueField requires the user make a selection
41271      * in order for a value to be mapped.
41272      */
41273     valueField: undefined,
41274     
41275     
41276     /**
41277      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41278      * field's data value (defaults to the underlying DOM element's name)
41279      */
41280     hiddenName: undefined,
41281     /**
41282      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41283      */
41284     listClass: '',
41285     /**
41286      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41287      */
41288     selectedClass: 'x-combo-selected',
41289     /**
41290      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41291      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41292      * which displays a downward arrow icon).
41293      */
41294     triggerClass : 'x-form-arrow-trigger',
41295     /**
41296      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41297      */
41298     shadow:'sides',
41299     /**
41300      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41301      * anchor positions (defaults to 'tl-bl')
41302      */
41303     listAlign: 'tl-bl?',
41304     /**
41305      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41306      */
41307     maxHeight: 300,
41308     /**
41309      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41310      * query specified by the allQuery config option (defaults to 'query')
41311      */
41312     triggerAction: 'query',
41313     /**
41314      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41315      * (defaults to 4, does not apply if editable = false)
41316      */
41317     minChars : 4,
41318     /**
41319      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41320      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41321      */
41322     typeAhead: false,
41323     /**
41324      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41325      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41326      */
41327     queryDelay: 500,
41328     /**
41329      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41330      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41331      */
41332     pageSize: 0,
41333     /**
41334      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41335      * when editable = true (defaults to false)
41336      */
41337     selectOnFocus:false,
41338     /**
41339      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41340      */
41341     queryParam: 'query',
41342     /**
41343      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41344      * when mode = 'remote' (defaults to 'Loading...')
41345      */
41346     loadingText: 'Loading...',
41347     /**
41348      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41349      */
41350     resizable: false,
41351     /**
41352      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41353      */
41354     handleHeight : 8,
41355     /**
41356      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41357      * traditional select (defaults to true)
41358      */
41359     editable: true,
41360     /**
41361      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41362      */
41363     allQuery: '',
41364     /**
41365      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41366      */
41367     mode: 'remote',
41368     /**
41369      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41370      * listWidth has a higher value)
41371      */
41372     minListWidth : 70,
41373     /**
41374      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41375      * allow the user to set arbitrary text into the field (defaults to false)
41376      */
41377     forceSelection:false,
41378     /**
41379      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41380      * if typeAhead = true (defaults to 250)
41381      */
41382     typeAheadDelay : 250,
41383     /**
41384      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41385      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41386      */
41387     valueNotFoundText : undefined,
41388     /**
41389      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41390      */
41391     blockFocus : false,
41392     
41393     /**
41394      * @cfg {Boolean} disableClear Disable showing of clear button.
41395      */
41396     disableClear : false,
41397     /**
41398      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41399      */
41400     alwaysQuery : false,
41401     
41402     //private
41403     addicon : false,
41404     editicon: false,
41405     
41406     // element that contains real text value.. (when hidden is used..)
41407      
41408     // private
41409     onRender : function(ct, position)
41410     {
41411         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41412         
41413         if(this.hiddenName){
41414             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41415                     'before', true);
41416             this.hiddenField.value =
41417                 this.hiddenValue !== undefined ? this.hiddenValue :
41418                 this.value !== undefined ? this.value : '';
41419
41420             // prevent input submission
41421             this.el.dom.removeAttribute('name');
41422              
41423              
41424         }
41425         
41426         if(Roo.isGecko){
41427             this.el.dom.setAttribute('autocomplete', 'off');
41428         }
41429
41430         var cls = 'x-combo-list';
41431
41432         this.list = new Roo.Layer({
41433             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41434         });
41435
41436         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41437         this.list.setWidth(lw);
41438         this.list.swallowEvent('mousewheel');
41439         this.assetHeight = 0;
41440
41441         if(this.title){
41442             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41443             this.assetHeight += this.header.getHeight();
41444         }
41445
41446         this.innerList = this.list.createChild({cls:cls+'-inner'});
41447         this.innerList.on('mouseover', this.onViewOver, this);
41448         this.innerList.on('mousemove', this.onViewMove, this);
41449         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41450         
41451         if(this.allowBlank && !this.pageSize && !this.disableClear){
41452             this.footer = this.list.createChild({cls:cls+'-ft'});
41453             this.pageTb = new Roo.Toolbar(this.footer);
41454            
41455         }
41456         if(this.pageSize){
41457             this.footer = this.list.createChild({cls:cls+'-ft'});
41458             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41459                     {pageSize: this.pageSize});
41460             
41461         }
41462         
41463         if (this.pageTb && this.allowBlank && !this.disableClear) {
41464             var _this = this;
41465             this.pageTb.add(new Roo.Toolbar.Fill(), {
41466                 cls: 'x-btn-icon x-btn-clear',
41467                 text: '&#160;',
41468                 handler: function()
41469                 {
41470                     _this.collapse();
41471                     _this.clearValue();
41472                     _this.onSelect(false, -1);
41473                 }
41474             });
41475         }
41476         if (this.footer) {
41477             this.assetHeight += this.footer.getHeight();
41478         }
41479         
41480
41481         if(!this.tpl){
41482             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41483         }
41484
41485         this.view = new Roo.View(this.innerList, this.tpl, {
41486             singleSelect:true,
41487             store: this.store,
41488             selectedClass: this.selectedClass
41489         });
41490
41491         this.view.on('click', this.onViewClick, this);
41492
41493         this.store.on('beforeload', this.onBeforeLoad, this);
41494         this.store.on('load', this.onLoad, this);
41495         this.store.on('loadexception', this.onLoadException, this);
41496
41497         if(this.resizable){
41498             this.resizer = new Roo.Resizable(this.list,  {
41499                pinned:true, handles:'se'
41500             });
41501             this.resizer.on('resize', function(r, w, h){
41502                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41503                 this.listWidth = w;
41504                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41505                 this.restrictHeight();
41506             }, this);
41507             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41508         }
41509         if(!this.editable){
41510             this.editable = true;
41511             this.setEditable(false);
41512         }  
41513         
41514         
41515         if (typeof(this.events.add.listeners) != 'undefined') {
41516             
41517             this.addicon = this.wrap.createChild(
41518                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41519        
41520             this.addicon.on('click', function(e) {
41521                 this.fireEvent('add', this);
41522             }, this);
41523         }
41524         if (typeof(this.events.edit.listeners) != 'undefined') {
41525             
41526             this.editicon = this.wrap.createChild(
41527                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41528             if (this.addicon) {
41529                 this.editicon.setStyle('margin-left', '40px');
41530             }
41531             this.editicon.on('click', function(e) {
41532                 
41533                 // we fire even  if inothing is selected..
41534                 this.fireEvent('edit', this, this.lastData );
41535                 
41536             }, this);
41537         }
41538         
41539         
41540         
41541     },
41542
41543     // private
41544     initEvents : function(){
41545         Roo.form.ComboBox.superclass.initEvents.call(this);
41546
41547         this.keyNav = new Roo.KeyNav(this.el, {
41548             "up" : function(e){
41549                 this.inKeyMode = true;
41550                 this.selectPrev();
41551             },
41552
41553             "down" : function(e){
41554                 if(!this.isExpanded()){
41555                     this.onTriggerClick();
41556                 }else{
41557                     this.inKeyMode = true;
41558                     this.selectNext();
41559                 }
41560             },
41561
41562             "enter" : function(e){
41563                 this.onViewClick();
41564                 //return true;
41565             },
41566
41567             "esc" : function(e){
41568                 this.collapse();
41569             },
41570
41571             "tab" : function(e){
41572                 this.onViewClick(false);
41573                 this.fireEvent("specialkey", this, e);
41574                 return true;
41575             },
41576
41577             scope : this,
41578
41579             doRelay : function(foo, bar, hname){
41580                 if(hname == 'down' || this.scope.isExpanded()){
41581                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41582                 }
41583                 return true;
41584             },
41585
41586             forceKeyDown: true
41587         });
41588         this.queryDelay = Math.max(this.queryDelay || 10,
41589                 this.mode == 'local' ? 10 : 250);
41590         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41591         if(this.typeAhead){
41592             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41593         }
41594         if(this.editable !== false){
41595             this.el.on("keyup", this.onKeyUp, this);
41596         }
41597         if(this.forceSelection){
41598             this.on('blur', this.doForce, this);
41599         }
41600     },
41601
41602     onDestroy : function(){
41603         if(this.view){
41604             this.view.setStore(null);
41605             this.view.el.removeAllListeners();
41606             this.view.el.remove();
41607             this.view.purgeListeners();
41608         }
41609         if(this.list){
41610             this.list.destroy();
41611         }
41612         if(this.store){
41613             this.store.un('beforeload', this.onBeforeLoad, this);
41614             this.store.un('load', this.onLoad, this);
41615             this.store.un('loadexception', this.onLoadException, this);
41616         }
41617         Roo.form.ComboBox.superclass.onDestroy.call(this);
41618     },
41619
41620     // private
41621     fireKey : function(e){
41622         if(e.isNavKeyPress() && !this.list.isVisible()){
41623             this.fireEvent("specialkey", this, e);
41624         }
41625     },
41626
41627     // private
41628     onResize: function(w, h){
41629         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41630         
41631         if(typeof w != 'number'){
41632             // we do not handle it!?!?
41633             return;
41634         }
41635         var tw = this.trigger.getWidth();
41636         tw += this.addicon ? this.addicon.getWidth() : 0;
41637         tw += this.editicon ? this.editicon.getWidth() : 0;
41638         var x = w - tw;
41639         this.el.setWidth( this.adjustWidth('input', x));
41640             
41641         this.trigger.setStyle('left', x+'px');
41642         
41643         if(this.list && this.listWidth === undefined){
41644             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41645             this.list.setWidth(lw);
41646             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41647         }
41648         
41649     
41650         
41651     },
41652
41653     /**
41654      * Allow or prevent the user from directly editing the field text.  If false is passed,
41655      * the user will only be able to select from the items defined in the dropdown list.  This method
41656      * is the runtime equivalent of setting the 'editable' config option at config time.
41657      * @param {Boolean} value True to allow the user to directly edit the field text
41658      */
41659     setEditable : function(value){
41660         if(value == this.editable){
41661             return;
41662         }
41663         this.editable = value;
41664         if(!value){
41665             this.el.dom.setAttribute('readOnly', true);
41666             this.el.on('mousedown', this.onTriggerClick,  this);
41667             this.el.addClass('x-combo-noedit');
41668         }else{
41669             this.el.dom.setAttribute('readOnly', false);
41670             this.el.un('mousedown', this.onTriggerClick,  this);
41671             this.el.removeClass('x-combo-noedit');
41672         }
41673     },
41674
41675     // private
41676     onBeforeLoad : function(){
41677         if(!this.hasFocus){
41678             return;
41679         }
41680         this.innerList.update(this.loadingText ?
41681                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41682         this.restrictHeight();
41683         this.selectedIndex = -1;
41684     },
41685
41686     // private
41687     onLoad : function(){
41688         if(!this.hasFocus){
41689             return;
41690         }
41691         if(this.store.getCount() > 0){
41692             this.expand();
41693             this.restrictHeight();
41694             if(this.lastQuery == this.allQuery){
41695                 if(this.editable){
41696                     this.el.dom.select();
41697                 }
41698                 if(!this.selectByValue(this.value, true)){
41699                     this.select(0, true);
41700                 }
41701             }else{
41702                 this.selectNext();
41703                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41704                     this.taTask.delay(this.typeAheadDelay);
41705                 }
41706             }
41707         }else{
41708             this.onEmptyResults();
41709         }
41710         //this.el.focus();
41711     },
41712     // private
41713     onLoadException : function()
41714     {
41715         this.collapse();
41716         Roo.log(this.store.reader.jsonData);
41717         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41718             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41719         }
41720         
41721         
41722     },
41723     // private
41724     onTypeAhead : function(){
41725         if(this.store.getCount() > 0){
41726             var r = this.store.getAt(0);
41727             var newValue = r.data[this.displayField];
41728             var len = newValue.length;
41729             var selStart = this.getRawValue().length;
41730             if(selStart != len){
41731                 this.setRawValue(newValue);
41732                 this.selectText(selStart, newValue.length);
41733             }
41734         }
41735     },
41736
41737     // private
41738     onSelect : function(record, index){
41739         if(this.fireEvent('beforeselect', this, record, index) !== false){
41740             this.setFromData(index > -1 ? record.data : false);
41741             this.collapse();
41742             this.fireEvent('select', this, record, index);
41743         }
41744     },
41745
41746     /**
41747      * Returns the currently selected field value or empty string if no value is set.
41748      * @return {String} value The selected value
41749      */
41750     getValue : function(){
41751         if(this.valueField){
41752             return typeof this.value != 'undefined' ? this.value : '';
41753         }
41754         return Roo.form.ComboBox.superclass.getValue.call(this);
41755     },
41756
41757     /**
41758      * Clears any text/value currently set in the field
41759      */
41760     clearValue : function(){
41761         if(this.hiddenField){
41762             this.hiddenField.value = '';
41763         }
41764         this.value = '';
41765         this.setRawValue('');
41766         this.lastSelectionText = '';
41767         
41768     },
41769
41770     /**
41771      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41772      * will be displayed in the field.  If the value does not match the data value of an existing item,
41773      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41774      * Otherwise the field will be blank (although the value will still be set).
41775      * @param {String} value The value to match
41776      */
41777     setValue : function(v){
41778         var text = v;
41779         if(this.valueField){
41780             var r = this.findRecord(this.valueField, v);
41781             if(r){
41782                 text = r.data[this.displayField];
41783             }else if(this.valueNotFoundText !== undefined){
41784                 text = this.valueNotFoundText;
41785             }
41786         }
41787         this.lastSelectionText = text;
41788         if(this.hiddenField){
41789             this.hiddenField.value = v;
41790         }
41791         Roo.form.ComboBox.superclass.setValue.call(this, text);
41792         this.value = v;
41793     },
41794     /**
41795      * @property {Object} the last set data for the element
41796      */
41797     
41798     lastData : false,
41799     /**
41800      * Sets the value of the field based on a object which is related to the record format for the store.
41801      * @param {Object} value the value to set as. or false on reset?
41802      */
41803     setFromData : function(o){
41804         var dv = ''; // display value
41805         var vv = ''; // value value..
41806         this.lastData = o;
41807         if (this.displayField) {
41808             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41809         } else {
41810             // this is an error condition!!!
41811             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41812         }
41813         
41814         if(this.valueField){
41815             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41816         }
41817         if(this.hiddenField){
41818             this.hiddenField.value = vv;
41819             
41820             this.lastSelectionText = dv;
41821             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41822             this.value = vv;
41823             return;
41824         }
41825         // no hidden field.. - we store the value in 'value', but still display
41826         // display field!!!!
41827         this.lastSelectionText = dv;
41828         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41829         this.value = vv;
41830         
41831         
41832     },
41833     // private
41834     reset : function(){
41835         // overridden so that last data is reset..
41836         this.setValue(this.resetValue);
41837         this.originalValue = this.getValue();
41838         this.clearInvalid();
41839         this.lastData = false;
41840         if (this.view) {
41841             this.view.clearSelections();
41842         }
41843     },
41844     // private
41845     findRecord : function(prop, value){
41846         var record;
41847         if(this.store.getCount() > 0){
41848             this.store.each(function(r){
41849                 if(r.data[prop] == value){
41850                     record = r;
41851                     return false;
41852                 }
41853                 return true;
41854             });
41855         }
41856         return record;
41857     },
41858     
41859     getName: function()
41860     {
41861         // returns hidden if it's set..
41862         if (!this.rendered) {return ''};
41863         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41864         
41865     },
41866     // private
41867     onViewMove : function(e, t){
41868         this.inKeyMode = false;
41869     },
41870
41871     // private
41872     onViewOver : function(e, t){
41873         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41874             return;
41875         }
41876         var item = this.view.findItemFromChild(t);
41877         if(item){
41878             var index = this.view.indexOf(item);
41879             this.select(index, false);
41880         }
41881     },
41882
41883     // private
41884     onViewClick : function(doFocus)
41885     {
41886         var index = this.view.getSelectedIndexes()[0];
41887         var r = this.store.getAt(index);
41888         if(r){
41889             this.onSelect(r, index);
41890         }
41891         if(doFocus !== false && !this.blockFocus){
41892             this.el.focus();
41893         }
41894     },
41895
41896     // private
41897     restrictHeight : function(){
41898         this.innerList.dom.style.height = '';
41899         var inner = this.innerList.dom;
41900         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41901         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41902         this.list.beginUpdate();
41903         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41904         this.list.alignTo(this.el, this.listAlign);
41905         this.list.endUpdate();
41906     },
41907
41908     // private
41909     onEmptyResults : function(){
41910         this.collapse();
41911     },
41912
41913     /**
41914      * Returns true if the dropdown list is expanded, else false.
41915      */
41916     isExpanded : function(){
41917         return this.list.isVisible();
41918     },
41919
41920     /**
41921      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41922      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41923      * @param {String} value The data value of the item to select
41924      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41925      * selected item if it is not currently in view (defaults to true)
41926      * @return {Boolean} True if the value matched an item in the list, else false
41927      */
41928     selectByValue : function(v, scrollIntoView){
41929         if(v !== undefined && v !== null){
41930             var r = this.findRecord(this.valueField || this.displayField, v);
41931             if(r){
41932                 this.select(this.store.indexOf(r), scrollIntoView);
41933                 return true;
41934             }
41935         }
41936         return false;
41937     },
41938
41939     /**
41940      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41941      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41942      * @param {Number} index The zero-based index of the list item to select
41943      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41944      * selected item if it is not currently in view (defaults to true)
41945      */
41946     select : function(index, scrollIntoView){
41947         this.selectedIndex = index;
41948         this.view.select(index);
41949         if(scrollIntoView !== false){
41950             var el = this.view.getNode(index);
41951             if(el){
41952                 this.innerList.scrollChildIntoView(el, false);
41953             }
41954         }
41955     },
41956
41957     // private
41958     selectNext : function(){
41959         var ct = this.store.getCount();
41960         if(ct > 0){
41961             if(this.selectedIndex == -1){
41962                 this.select(0);
41963             }else if(this.selectedIndex < ct-1){
41964                 this.select(this.selectedIndex+1);
41965             }
41966         }
41967     },
41968
41969     // private
41970     selectPrev : function(){
41971         var ct = this.store.getCount();
41972         if(ct > 0){
41973             if(this.selectedIndex == -1){
41974                 this.select(0);
41975             }else if(this.selectedIndex != 0){
41976                 this.select(this.selectedIndex-1);
41977             }
41978         }
41979     },
41980
41981     // private
41982     onKeyUp : function(e){
41983         if(this.editable !== false && !e.isSpecialKey()){
41984             this.lastKey = e.getKey();
41985             this.dqTask.delay(this.queryDelay);
41986         }
41987     },
41988
41989     // private
41990     validateBlur : function(){
41991         return !this.list || !this.list.isVisible();   
41992     },
41993
41994     // private
41995     initQuery : function(){
41996         this.doQuery(this.getRawValue());
41997     },
41998
41999     // private
42000     doForce : function(){
42001         if(this.el.dom.value.length > 0){
42002             this.el.dom.value =
42003                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42004              
42005         }
42006     },
42007
42008     /**
42009      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42010      * query allowing the query action to be canceled if needed.
42011      * @param {String} query The SQL query to execute
42012      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42013      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42014      * saved in the current store (defaults to false)
42015      */
42016     doQuery : function(q, forceAll){
42017         if(q === undefined || q === null){
42018             q = '';
42019         }
42020         var qe = {
42021             query: q,
42022             forceAll: forceAll,
42023             combo: this,
42024             cancel:false
42025         };
42026         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42027             return false;
42028         }
42029         q = qe.query;
42030         forceAll = qe.forceAll;
42031         if(forceAll === true || (q.length >= this.minChars)){
42032             if(this.lastQuery != q || this.alwaysQuery){
42033                 this.lastQuery = q;
42034                 if(this.mode == 'local'){
42035                     this.selectedIndex = -1;
42036                     if(forceAll){
42037                         this.store.clearFilter();
42038                     }else{
42039                         this.store.filter(this.displayField, q);
42040                     }
42041                     this.onLoad();
42042                 }else{
42043                     this.store.baseParams[this.queryParam] = q;
42044                     this.store.load({
42045                         params: this.getParams(q)
42046                     });
42047                     this.expand();
42048                 }
42049             }else{
42050                 this.selectedIndex = -1;
42051                 this.onLoad();   
42052             }
42053         }
42054     },
42055
42056     // private
42057     getParams : function(q){
42058         var p = {};
42059         //p[this.queryParam] = q;
42060         if(this.pageSize){
42061             p.start = 0;
42062             p.limit = this.pageSize;
42063         }
42064         return p;
42065     },
42066
42067     /**
42068      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42069      */
42070     collapse : function(){
42071         if(!this.isExpanded()){
42072             return;
42073         }
42074         this.list.hide();
42075         Roo.get(document).un('mousedown', this.collapseIf, this);
42076         Roo.get(document).un('mousewheel', this.collapseIf, this);
42077         if (!this.editable) {
42078             Roo.get(document).un('keydown', this.listKeyPress, this);
42079         }
42080         this.fireEvent('collapse', this);
42081     },
42082
42083     // private
42084     collapseIf : function(e){
42085         if(!e.within(this.wrap) && !e.within(this.list)){
42086             this.collapse();
42087         }
42088     },
42089
42090     /**
42091      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42092      */
42093     expand : function(){
42094         if(this.isExpanded() || !this.hasFocus){
42095             return;
42096         }
42097         this.list.alignTo(this.el, this.listAlign);
42098         this.list.show();
42099         Roo.get(document).on('mousedown', this.collapseIf, this);
42100         Roo.get(document).on('mousewheel', this.collapseIf, this);
42101         if (!this.editable) {
42102             Roo.get(document).on('keydown', this.listKeyPress, this);
42103         }
42104         
42105         this.fireEvent('expand', this);
42106     },
42107
42108     // private
42109     // Implements the default empty TriggerField.onTriggerClick function
42110     onTriggerClick : function(){
42111         if(this.disabled){
42112             return;
42113         }
42114         if(this.isExpanded()){
42115             this.collapse();
42116             if (!this.blockFocus) {
42117                 this.el.focus();
42118             }
42119             
42120         }else {
42121             this.hasFocus = true;
42122             if(this.triggerAction == 'all') {
42123                 this.doQuery(this.allQuery, true);
42124             } else {
42125                 this.doQuery(this.getRawValue());
42126             }
42127             if (!this.blockFocus) {
42128                 this.el.focus();
42129             }
42130         }
42131     },
42132     listKeyPress : function(e)
42133     {
42134         //Roo.log('listkeypress');
42135         // scroll to first matching element based on key pres..
42136         if (e.isSpecialKey()) {
42137             return false;
42138         }
42139         var k = String.fromCharCode(e.getKey()).toUpperCase();
42140         //Roo.log(k);
42141         var match  = false;
42142         var csel = this.view.getSelectedNodes();
42143         var cselitem = false;
42144         if (csel.length) {
42145             var ix = this.view.indexOf(csel[0]);
42146             cselitem  = this.store.getAt(ix);
42147             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42148                 cselitem = false;
42149             }
42150             
42151         }
42152         
42153         this.store.each(function(v) { 
42154             if (cselitem) {
42155                 // start at existing selection.
42156                 if (cselitem.id == v.id) {
42157                     cselitem = false;
42158                 }
42159                 return;
42160             }
42161                 
42162             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42163                 match = this.store.indexOf(v);
42164                 return false;
42165             }
42166         }, this);
42167         
42168         if (match === false) {
42169             return true; // no more action?
42170         }
42171         // scroll to?
42172         this.view.select(match);
42173         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42174         sn.scrollIntoView(sn.dom.parentNode, false);
42175     } 
42176
42177     /** 
42178     * @cfg {Boolean} grow 
42179     * @hide 
42180     */
42181     /** 
42182     * @cfg {Number} growMin 
42183     * @hide 
42184     */
42185     /** 
42186     * @cfg {Number} growMax 
42187     * @hide 
42188     */
42189     /**
42190      * @hide
42191      * @method autoSize
42192      */
42193 });/*
42194  * Copyright(c) 2010-2012, Roo J Solutions Limited
42195  *
42196  * Licence LGPL
42197  *
42198  */
42199
42200 /**
42201  * @class Roo.form.ComboBoxArray
42202  * @extends Roo.form.TextField
42203  * A facebook style adder... for lists of email / people / countries  etc...
42204  * pick multiple items from a combo box, and shows each one.
42205  *
42206  *  Fred [x]  Brian [x]  [Pick another |v]
42207  *
42208  *
42209  *  For this to work: it needs various extra information
42210  *    - normal combo problay has
42211  *      name, hiddenName
42212  *    + displayField, valueField
42213  *
42214  *    For our purpose...
42215  *
42216  *
42217  *   If we change from 'extends' to wrapping...
42218  *   
42219  *  
42220  *
42221  
42222  
42223  * @constructor
42224  * Create a new ComboBoxArray.
42225  * @param {Object} config Configuration options
42226  */
42227  
42228
42229 Roo.form.ComboBoxArray = function(config)
42230 {
42231     this.addEvents({
42232         /**
42233          * @event beforeremove
42234          * Fires before remove the value from the list
42235              * @param {Roo.form.ComboBoxArray} _self This combo box array
42236              * @param {Roo.form.ComboBoxArray.Item} item removed item
42237              */
42238         'beforeremove' : true,
42239         /**
42240          * @event remove
42241          * Fires when remove the value from the list
42242              * @param {Roo.form.ComboBoxArray} _self This combo box array
42243              * @param {Roo.form.ComboBoxArray.Item} item removed item
42244              */
42245         'remove' : true
42246         
42247         
42248     });
42249     
42250     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42251     
42252     this.items = new Roo.util.MixedCollection(false);
42253     
42254     // construct the child combo...
42255     
42256     
42257     
42258     
42259    
42260     
42261 }
42262
42263  
42264 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42265
42266     /**
42267      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42268      */
42269     
42270     lastData : false,
42271     
42272     // behavies liek a hiddne field
42273     inputType:      'hidden',
42274     /**
42275      * @cfg {Number} width The width of the box that displays the selected element
42276      */ 
42277     width:          300,
42278
42279     
42280     
42281     /**
42282      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42283      */
42284     name : false,
42285     /**
42286      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42287      */
42288     hiddenName : false,
42289     
42290     
42291     // private the array of items that are displayed..
42292     items  : false,
42293     // private - the hidden field el.
42294     hiddenEl : false,
42295     // private - the filed el..
42296     el : false,
42297     
42298     //validateValue : function() { return true; }, // all values are ok!
42299     //onAddClick: function() { },
42300     
42301     onRender : function(ct, position) 
42302     {
42303         
42304         // create the standard hidden element
42305         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42306         
42307         
42308         // give fake names to child combo;
42309         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42310         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42311         
42312         this.combo = Roo.factory(this.combo, Roo.form);
42313         this.combo.onRender(ct, position);
42314         if (typeof(this.combo.width) != 'undefined') {
42315             this.combo.onResize(this.combo.width,0);
42316         }
42317         
42318         this.combo.initEvents();
42319         
42320         // assigned so form know we need to do this..
42321         this.store          = this.combo.store;
42322         this.valueField     = this.combo.valueField;
42323         this.displayField   = this.combo.displayField ;
42324         
42325         
42326         this.combo.wrap.addClass('x-cbarray-grp');
42327         
42328         var cbwrap = this.combo.wrap.createChild(
42329             {tag: 'div', cls: 'x-cbarray-cb'},
42330             this.combo.el.dom
42331         );
42332         
42333              
42334         this.hiddenEl = this.combo.wrap.createChild({
42335             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42336         });
42337         this.el = this.combo.wrap.createChild({
42338             tag: 'input',  type:'hidden' , name: this.name, value : ''
42339         });
42340          //   this.el.dom.removeAttribute("name");
42341         
42342         
42343         this.outerWrap = this.combo.wrap;
42344         this.wrap = cbwrap;
42345         
42346         this.outerWrap.setWidth(this.width);
42347         this.outerWrap.dom.removeChild(this.el.dom);
42348         
42349         this.wrap.dom.appendChild(this.el.dom);
42350         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42351         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42352         
42353         this.combo.trigger.setStyle('position','relative');
42354         this.combo.trigger.setStyle('left', '0px');
42355         this.combo.trigger.setStyle('top', '2px');
42356         
42357         this.combo.el.setStyle('vertical-align', 'text-bottom');
42358         
42359         //this.trigger.setStyle('vertical-align', 'top');
42360         
42361         // this should use the code from combo really... on('add' ....)
42362         if (this.adder) {
42363             
42364         
42365             this.adder = this.outerWrap.createChild(
42366                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42367             var _t = this;
42368             this.adder.on('click', function(e) {
42369                 _t.fireEvent('adderclick', this, e);
42370             }, _t);
42371         }
42372         //var _t = this;
42373         //this.adder.on('click', this.onAddClick, _t);
42374         
42375         
42376         this.combo.on('select', function(cb, rec, ix) {
42377             this.addItem(rec.data);
42378             
42379             cb.setValue('');
42380             cb.el.dom.value = '';
42381             //cb.lastData = rec.data;
42382             // add to list
42383             
42384         }, this);
42385         
42386         
42387     },
42388     
42389     
42390     getName: function()
42391     {
42392         // returns hidden if it's set..
42393         if (!this.rendered) {return ''};
42394         return  this.hiddenName ? this.hiddenName : this.name;
42395         
42396     },
42397     
42398     
42399     onResize: function(w, h){
42400         
42401         return;
42402         // not sure if this is needed..
42403         //this.combo.onResize(w,h);
42404         
42405         if(typeof w != 'number'){
42406             // we do not handle it!?!?
42407             return;
42408         }
42409         var tw = this.combo.trigger.getWidth();
42410         tw += this.addicon ? this.addicon.getWidth() : 0;
42411         tw += this.editicon ? this.editicon.getWidth() : 0;
42412         var x = w - tw;
42413         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42414             
42415         this.combo.trigger.setStyle('left', '0px');
42416         
42417         if(this.list && this.listWidth === undefined){
42418             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42419             this.list.setWidth(lw);
42420             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42421         }
42422         
42423     
42424         
42425     },
42426     
42427     addItem: function(rec)
42428     {
42429         var valueField = this.combo.valueField;
42430         var displayField = this.combo.displayField;
42431         
42432         if (this.items.indexOfKey(rec[valueField]) > -1) {
42433             //console.log("GOT " + rec.data.id);
42434             return;
42435         }
42436         
42437         var x = new Roo.form.ComboBoxArray.Item({
42438             //id : rec[this.idField],
42439             data : rec,
42440             displayField : displayField ,
42441             tipField : displayField ,
42442             cb : this
42443         });
42444         // use the 
42445         this.items.add(rec[valueField],x);
42446         // add it before the element..
42447         this.updateHiddenEl();
42448         x.render(this.outerWrap, this.wrap.dom);
42449         // add the image handler..
42450     },
42451     
42452     updateHiddenEl : function()
42453     {
42454         this.validate();
42455         if (!this.hiddenEl) {
42456             return;
42457         }
42458         var ar = [];
42459         var idField = this.combo.valueField;
42460         
42461         this.items.each(function(f) {
42462             ar.push(f.data[idField]);
42463         });
42464         this.hiddenEl.dom.value = ar.join(',');
42465         this.validate();
42466     },
42467     
42468     reset : function()
42469     {
42470         this.items.clear();
42471         
42472         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42473            el.remove();
42474         });
42475         
42476         this.el.dom.value = '';
42477         if (this.hiddenEl) {
42478             this.hiddenEl.dom.value = '';
42479         }
42480         
42481     },
42482     getValue: function()
42483     {
42484         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42485     },
42486     setValue: function(v) // not a valid action - must use addItems..
42487     {
42488         
42489         this.reset();
42490          
42491         if (this.store.isLocal && (typeof(v) == 'string')) {
42492             // then we can use the store to find the values..
42493             // comma seperated at present.. this needs to allow JSON based encoding..
42494             this.hiddenEl.value  = v;
42495             var v_ar = [];
42496             Roo.each(v.split(','), function(k) {
42497                 Roo.log("CHECK " + this.valueField + ',' + k);
42498                 var li = this.store.query(this.valueField, k);
42499                 if (!li.length) {
42500                     return;
42501                 }
42502                 var add = {};
42503                 add[this.valueField] = k;
42504                 add[this.displayField] = li.item(0).data[this.displayField];
42505                 
42506                 this.addItem(add);
42507             }, this) 
42508              
42509         }
42510         if (typeof(v) == 'object' ) {
42511             // then let's assume it's an array of objects..
42512             Roo.each(v, function(l) {
42513                 this.addItem(l);
42514             }, this);
42515              
42516         }
42517         
42518         
42519     },
42520     setFromData: function(v)
42521     {
42522         // this recieves an object, if setValues is called.
42523         this.reset();
42524         this.el.dom.value = v[this.displayField];
42525         this.hiddenEl.dom.value = v[this.valueField];
42526         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42527             return;
42528         }
42529         var kv = v[this.valueField];
42530         var dv = v[this.displayField];
42531         kv = typeof(kv) != 'string' ? '' : kv;
42532         dv = typeof(dv) != 'string' ? '' : dv;
42533         
42534         
42535         var keys = kv.split(',');
42536         var display = dv.split(',');
42537         for (var i = 0 ; i < keys.length; i++) {
42538             
42539             add = {};
42540             add[this.valueField] = keys[i];
42541             add[this.displayField] = display[i];
42542             this.addItem(add);
42543         }
42544       
42545         
42546     },
42547     
42548     /**
42549      * Validates the combox array value
42550      * @return {Boolean} True if the value is valid, else false
42551      */
42552     validate : function(){
42553         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42554             this.clearInvalid();
42555             return true;
42556         }
42557         return false;
42558     },
42559     
42560     validateValue : function(value){
42561         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42562         
42563     },
42564     
42565     /*@
42566      * overide
42567      * 
42568      */
42569     isDirty : function() {
42570         if(this.disabled) {
42571             return false;
42572         }
42573         
42574         try {
42575             var d = Roo.decode(String(this.originalValue));
42576         } catch (e) {
42577             return String(this.getValue()) !== String(this.originalValue);
42578         }
42579         
42580         var originalValue = [];
42581         
42582         for (var i = 0; i < d.length; i++){
42583             originalValue.push(d[i][this.valueField]);
42584         }
42585         
42586         return String(this.getValue()) !== String(originalValue.join(','));
42587         
42588     }
42589     
42590 });
42591
42592
42593
42594 /**
42595  * @class Roo.form.ComboBoxArray.Item
42596  * @extends Roo.BoxComponent
42597  * A selected item in the list
42598  *  Fred [x]  Brian [x]  [Pick another |v]
42599  * 
42600  * @constructor
42601  * Create a new item.
42602  * @param {Object} config Configuration options
42603  */
42604  
42605 Roo.form.ComboBoxArray.Item = function(config) {
42606     config.id = Roo.id();
42607     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42608 }
42609
42610 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42611     data : {},
42612     cb: false,
42613     displayField : false,
42614     tipField : false,
42615     
42616     
42617     defaultAutoCreate : {
42618         tag: 'div',
42619         cls: 'x-cbarray-item',
42620         cn : [ 
42621             { tag: 'div' },
42622             {
42623                 tag: 'img',
42624                 width:16,
42625                 height : 16,
42626                 src : Roo.BLANK_IMAGE_URL ,
42627                 align: 'center'
42628             }
42629         ]
42630         
42631     },
42632     
42633  
42634     onRender : function(ct, position)
42635     {
42636         Roo.form.Field.superclass.onRender.call(this, ct, position);
42637         
42638         if(!this.el){
42639             var cfg = this.getAutoCreate();
42640             this.el = ct.createChild(cfg, position);
42641         }
42642         
42643         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42644         
42645         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42646             this.cb.renderer(this.data) :
42647             String.format('{0}',this.data[this.displayField]);
42648         
42649             
42650         this.el.child('div').dom.setAttribute('qtip',
42651                         String.format('{0}',this.data[this.tipField])
42652         );
42653         
42654         this.el.child('img').on('click', this.remove, this);
42655         
42656     },
42657    
42658     remove : function()
42659     {
42660         if(this.cb.disabled){
42661             return;
42662         }
42663         
42664         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42665             this.cb.items.remove(this);
42666             this.el.child('img').un('click', this.remove, this);
42667             this.el.remove();
42668             this.cb.updateHiddenEl();
42669
42670             this.cb.fireEvent('remove', this.cb, this);
42671         }
42672         
42673     }
42674 });/*
42675  * RooJS Library 1.1.1
42676  * Copyright(c) 2008-2011  Alan Knowles
42677  *
42678  * License - LGPL
42679  */
42680  
42681
42682 /**
42683  * @class Roo.form.ComboNested
42684  * @extends Roo.form.ComboBox
42685  * A combobox for that allows selection of nested items in a list,
42686  * eg.
42687  *
42688  *  Book
42689  *    -> red
42690  *    -> green
42691  *  Table
42692  *    -> square
42693  *      ->red
42694  *      ->green
42695  *    -> rectangle
42696  *      ->green
42697  *      
42698  * 
42699  * @constructor
42700  * Create a new ComboNested
42701  * @param {Object} config Configuration options
42702  */
42703 Roo.form.ComboNested = function(config){
42704     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42705     // should verify some data...
42706     // like
42707     // hiddenName = required..
42708     // displayField = required
42709     // valudField == required
42710     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42711     var _t = this;
42712     Roo.each(req, function(e) {
42713         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42714             throw "Roo.form.ComboNested : missing value for: " + e;
42715         }
42716     });
42717      
42718     
42719 };
42720
42721 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42722    
42723     /*
42724      * @config {Number} max Number of columns to show
42725      */
42726     
42727     maxColumns : 3,
42728    
42729     list : null, // the outermost div..
42730     innerLists : null, // the
42731     views : null,
42732     stores : null,
42733     // private
42734     onRender : function(ct, position)
42735     {
42736         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42737         
42738         if(this.hiddenName){
42739             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42740                     'before', true);
42741             this.hiddenField.value =
42742                 this.hiddenValue !== undefined ? this.hiddenValue :
42743                 this.value !== undefined ? this.value : '';
42744
42745             // prevent input submission
42746             this.el.dom.removeAttribute('name');
42747              
42748              
42749         }
42750         
42751         if(Roo.isGecko){
42752             this.el.dom.setAttribute('autocomplete', 'off');
42753         }
42754
42755         var cls = 'x-combo-list';
42756
42757         this.list = new Roo.Layer({
42758             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42759         });
42760
42761         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42762         this.list.setWidth(lw);
42763         this.list.swallowEvent('mousewheel');
42764         this.assetHeight = 0;
42765
42766         if(this.title){
42767             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42768             this.assetHeight += this.header.getHeight();
42769         }
42770         this.innerLists = [];
42771         this.views = [];
42772         this.stores = [];
42773         for (var i =0 ; i < this.maxColumns; i++) {
42774             this.onRenderList( cls, i);
42775         }
42776         
42777         // always needs footer, as we are going to have an 'OK' button.
42778         this.footer = this.list.createChild({cls:cls+'-ft'});
42779         this.pageTb = new Roo.Toolbar(this.footer);  
42780         var _this = this;
42781         this.pageTb.add(  {
42782             
42783             text: 'Done',
42784             handler: function()
42785             {
42786                 _this.collapse();
42787             }
42788         });
42789         
42790         if ( this.allowBlank && !this.disableClear) {
42791             
42792             this.pageTb.add(new Roo.Toolbar.Fill(), {
42793                 cls: 'x-btn-icon x-btn-clear',
42794                 text: '&#160;',
42795                 handler: function()
42796                 {
42797                     _this.collapse();
42798                     _this.clearValue();
42799                     _this.onSelect(false, -1);
42800                 }
42801             });
42802         }
42803         if (this.footer) {
42804             this.assetHeight += this.footer.getHeight();
42805         }
42806         
42807     },
42808     onRenderList : function (  cls, i)
42809     {
42810         
42811         var lw = Math.floor(
42812                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42813         );
42814         
42815         this.list.setWidth(lw); // default to '1'
42816
42817         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42818         //il.on('mouseover', this.onViewOver, this, { list:  i });
42819         //il.on('mousemove', this.onViewMove, this, { list:  i });
42820         il.setWidth(lw);
42821         il.setStyle({ 'overflow-x' : 'hidden'});
42822
42823         if(!this.tpl){
42824             this.tpl = new Roo.Template({
42825                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42826                 isEmpty: function (value, allValues) {
42827                     //Roo.log(value);
42828                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
42829                     return dl ? 'has-children' : 'no-children'
42830                 }
42831             });
42832         }
42833         
42834         var store  = this.store;
42835         if (i > 0) {
42836             store  = new Roo.data.SimpleStore({
42837                 //fields : this.store.reader.meta.fields,
42838                 reader : this.store.reader,
42839                 data : [ ]
42840             });
42841         }
42842         this.stores[i]  = store;
42843                 
42844         
42845         
42846         var view = this.views[i] = new Roo.View(
42847             il,
42848             this.tpl,
42849             {
42850                 singleSelect:true,
42851                 store: store,
42852                 selectedClass: this.selectedClass
42853             }
42854         );
42855         view.getEl().setWidth(lw);
42856         view.getEl().setStyle({
42857             position: i < 1 ? 'relative' : 'absolute',
42858             top: 0,
42859             left: (i * lw ) + 'px',
42860             display : i > 0 ? 'none' : 'block'
42861         });
42862         view.on('selectionchange', this.onSelectChange, this, {list : i });
42863         view.on('dblclick', this.onDoubleClick, this, {list : i });
42864         //view.on('click', this.onViewClick, this, { list : i });
42865
42866         store.on('beforeload', this.onBeforeLoad, this);
42867         store.on('load',  this.onLoad, this, { list  : i});
42868         store.on('loadexception', this.onLoadException, this);
42869
42870         // hide the other vies..
42871         
42872         
42873         
42874     },
42875     onResize : function()  {},
42876     
42877     restrictHeight : function()
42878     {
42879         var mh = 0;
42880         Roo.each(this.innerLists, function(il,i) {
42881             var el = this.views[i].getEl();
42882             el.dom.style.height = '';
42883             var inner = el.dom;
42884             var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42885             // only adjust heights on other ones..
42886             if (i < 1) {
42887                 
42888                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42889                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42890                 mh = Math.max(el.getHeight(), mh);
42891             }
42892             
42893             
42894         }, this);
42895         
42896         this.list.beginUpdate();
42897         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
42898         this.list.alignTo(this.el, this.listAlign);
42899         this.list.endUpdate();
42900         
42901     },
42902      
42903     
42904     // -- store handlers..
42905     // private
42906     onBeforeLoad : function()
42907     {
42908         if(!this.hasFocus){
42909             return;
42910         }
42911         this.innerLists[0].update(this.loadingText ?
42912                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42913         this.restrictHeight();
42914         this.selectedIndex = -1;
42915     },
42916     // private
42917     onLoad : function(a,b,c,d)
42918     {
42919         
42920         if(!this.hasFocus){
42921             return;
42922         }
42923         
42924         if(this.store.getCount() > 0) {
42925             this.expand();
42926             this.restrictHeight();   
42927         } else {
42928             this.onEmptyResults();
42929         }
42930         /*
42931         this.stores[1].loadData([]);
42932         this.stores[2].loadData([]);
42933         this.views
42934         */    
42935     
42936         //this.el.focus();
42937     },
42938     
42939     
42940     // private
42941     onLoadException : function()
42942     {
42943         this.collapse();
42944         Roo.log(this.store.reader.jsonData);
42945         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42946             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42947         }
42948         
42949         
42950     } ,
42951      
42952      
42953
42954     onSelectChange : function (view, sels, opts )
42955     {
42956         var ix = view.getSelectedIndexes();
42957         
42958         
42959         if (opts.list > this.maxColumns - 2) {
42960              
42961             this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
42962             return;
42963         }
42964         
42965         if (!ix.length) {
42966             this.setFromData({});
42967             this.stores[opts.list+1].loadData( [] );
42968             return;
42969         }
42970         
42971         var rec = view.store.getAt(ix[0]);
42972         this.setFromData(rec.data);
42973         
42974         var lw = Math.floor(
42975                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42976         );
42977         var data =  typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
42978         var dl = typeof(data.data) != 'undefined' ? data.total : data.length; ///json is a nested response..
42979         this.stores[opts.list+1].loadData( data );
42980         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
42981         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
42982         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
42983         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1))); 
42984     },
42985     onDoubleClick : function()
42986     {
42987         this.collapse(); //??
42988     },
42989     
42990      
42991     
42992     findRecord : function (prop,value)
42993     {
42994         return this.findRecordInStore(this.store, prop,value);
42995     },
42996     
42997      // private
42998     findRecordInStore : function(store, prop, value)
42999     {
43000         var cstore = new Roo.data.SimpleStore({
43001             //fields : this.store.reader.meta.fields, // we need array reader.. for
43002             reader : this.store.reader,
43003             data : [ ]
43004         });
43005         var _this = this;
43006         var record  = false;
43007         if(store.getCount() > 0){
43008            store.each(function(r){
43009                 if(r.data[prop] == value){
43010                     record = r;
43011                     return false;
43012                 }
43013                 if (r.data.cn && r.data.cn.length) {
43014                     cstore.loadData( r.data.cn);
43015                     var cret = _this.findRecordInStore(cstore, prop, value);
43016                     if (cret !== false) {
43017                         record = cret;
43018                         return false;
43019                     }
43020                 }
43021                 
43022                 return true;
43023             });
43024         }
43025         return record;
43026     }
43027     
43028     
43029     
43030     
43031 });/*
43032  * Based on:
43033  * Ext JS Library 1.1.1
43034  * Copyright(c) 2006-2007, Ext JS, LLC.
43035  *
43036  * Originally Released Under LGPL - original licence link has changed is not relivant.
43037  *
43038  * Fork - LGPL
43039  * <script type="text/javascript">
43040  */
43041 /**
43042  * @class Roo.form.Checkbox
43043  * @extends Roo.form.Field
43044  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43045  * @constructor
43046  * Creates a new Checkbox
43047  * @param {Object} config Configuration options
43048  */
43049 Roo.form.Checkbox = function(config){
43050     Roo.form.Checkbox.superclass.constructor.call(this, config);
43051     this.addEvents({
43052         /**
43053          * @event check
43054          * Fires when the checkbox is checked or unchecked.
43055              * @param {Roo.form.Checkbox} this This checkbox
43056              * @param {Boolean} checked The new checked value
43057              */
43058         check : true
43059     });
43060 };
43061
43062 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43063     /**
43064      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43065      */
43066     focusClass : undefined,
43067     /**
43068      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43069      */
43070     fieldClass: "x-form-field",
43071     /**
43072      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43073      */
43074     checked: false,
43075     /**
43076      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43077      * {tag: "input", type: "checkbox", autocomplete: "off"})
43078      */
43079     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43080     /**
43081      * @cfg {String} boxLabel The text that appears beside the checkbox
43082      */
43083     boxLabel : "",
43084     /**
43085      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43086      */  
43087     inputValue : '1',
43088     /**
43089      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43090      */
43091      valueOff: '0', // value when not checked..
43092
43093     actionMode : 'viewEl', 
43094     //
43095     // private
43096     itemCls : 'x-menu-check-item x-form-item',
43097     groupClass : 'x-menu-group-item',
43098     inputType : 'hidden',
43099     
43100     
43101     inSetChecked: false, // check that we are not calling self...
43102     
43103     inputElement: false, // real input element?
43104     basedOn: false, // ????
43105     
43106     isFormField: true, // not sure where this is needed!!!!
43107
43108     onResize : function(){
43109         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43110         if(!this.boxLabel){
43111             this.el.alignTo(this.wrap, 'c-c');
43112         }
43113     },
43114
43115     initEvents : function(){
43116         Roo.form.Checkbox.superclass.initEvents.call(this);
43117         this.el.on("click", this.onClick,  this);
43118         this.el.on("change", this.onClick,  this);
43119     },
43120
43121
43122     getResizeEl : function(){
43123         return this.wrap;
43124     },
43125
43126     getPositionEl : function(){
43127         return this.wrap;
43128     },
43129
43130     // private
43131     onRender : function(ct, position){
43132         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43133         /*
43134         if(this.inputValue !== undefined){
43135             this.el.dom.value = this.inputValue;
43136         }
43137         */
43138         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43139         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43140         var viewEl = this.wrap.createChild({ 
43141             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43142         this.viewEl = viewEl;   
43143         this.wrap.on('click', this.onClick,  this); 
43144         
43145         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43146         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43147         
43148         
43149         
43150         if(this.boxLabel){
43151             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43152         //    viewEl.on('click', this.onClick,  this); 
43153         }
43154         //if(this.checked){
43155             this.setChecked(this.checked);
43156         //}else{
43157             //this.checked = this.el.dom;
43158         //}
43159
43160     },
43161
43162     // private
43163     initValue : Roo.emptyFn,
43164
43165     /**
43166      * Returns the checked state of the checkbox.
43167      * @return {Boolean} True if checked, else false
43168      */
43169     getValue : function(){
43170         if(this.el){
43171             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43172         }
43173         return this.valueOff;
43174         
43175     },
43176
43177         // private
43178     onClick : function(){ 
43179         if (this.disabled) {
43180             return;
43181         }
43182         this.setChecked(!this.checked);
43183
43184         //if(this.el.dom.checked != this.checked){
43185         //    this.setValue(this.el.dom.checked);
43186        // }
43187     },
43188
43189     /**
43190      * Sets the checked state of the checkbox.
43191      * On is always based on a string comparison between inputValue and the param.
43192      * @param {Boolean/String} value - the value to set 
43193      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43194      */
43195     setValue : function(v,suppressEvent){
43196         
43197         
43198         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43199         //if(this.el && this.el.dom){
43200         //    this.el.dom.checked = this.checked;
43201         //    this.el.dom.defaultChecked = this.checked;
43202         //}
43203         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43204         //this.fireEvent("check", this, this.checked);
43205     },
43206     // private..
43207     setChecked : function(state,suppressEvent)
43208     {
43209         if (this.inSetChecked) {
43210             this.checked = state;
43211             return;
43212         }
43213         
43214     
43215         if(this.wrap){
43216             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43217         }
43218         this.checked = state;
43219         if(suppressEvent !== true){
43220             this.fireEvent('check', this, state);
43221         }
43222         this.inSetChecked = true;
43223         this.el.dom.value = state ? this.inputValue : this.valueOff;
43224         this.inSetChecked = false;
43225         
43226     },
43227     // handle setting of hidden value by some other method!!?!?
43228     setFromHidden: function()
43229     {
43230         if(!this.el){
43231             return;
43232         }
43233         //console.log("SET FROM HIDDEN");
43234         //alert('setFrom hidden');
43235         this.setValue(this.el.dom.value);
43236     },
43237     
43238     onDestroy : function()
43239     {
43240         if(this.viewEl){
43241             Roo.get(this.viewEl).remove();
43242         }
43243          
43244         Roo.form.Checkbox.superclass.onDestroy.call(this);
43245     },
43246     
43247     setBoxLabel : function(str)
43248     {
43249         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43250     }
43251
43252 });/*
43253  * Based on:
43254  * Ext JS Library 1.1.1
43255  * Copyright(c) 2006-2007, Ext JS, LLC.
43256  *
43257  * Originally Released Under LGPL - original licence link has changed is not relivant.
43258  *
43259  * Fork - LGPL
43260  * <script type="text/javascript">
43261  */
43262  
43263 /**
43264  * @class Roo.form.Radio
43265  * @extends Roo.form.Checkbox
43266  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43267  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43268  * @constructor
43269  * Creates a new Radio
43270  * @param {Object} config Configuration options
43271  */
43272 Roo.form.Radio = function(){
43273     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43274 };
43275 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43276     inputType: 'radio',
43277
43278     /**
43279      * If this radio is part of a group, it will return the selected value
43280      * @return {String}
43281      */
43282     getGroupValue : function(){
43283         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43284     },
43285     
43286     
43287     onRender : function(ct, position){
43288         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43289         
43290         if(this.inputValue !== undefined){
43291             this.el.dom.value = this.inputValue;
43292         }
43293          
43294         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43295         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43296         //var viewEl = this.wrap.createChild({ 
43297         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43298         //this.viewEl = viewEl;   
43299         //this.wrap.on('click', this.onClick,  this); 
43300         
43301         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43302         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43303         
43304         
43305         
43306         if(this.boxLabel){
43307             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43308         //    viewEl.on('click', this.onClick,  this); 
43309         }
43310          if(this.checked){
43311             this.el.dom.checked =   'checked' ;
43312         }
43313          
43314     } 
43315     
43316     
43317 });//<script type="text/javascript">
43318
43319 /*
43320  * Based  Ext JS Library 1.1.1
43321  * Copyright(c) 2006-2007, Ext JS, LLC.
43322  * LGPL
43323  *
43324  */
43325  
43326 /**
43327  * @class Roo.HtmlEditorCore
43328  * @extends Roo.Component
43329  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43330  *
43331  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43332  */
43333
43334 Roo.HtmlEditorCore = function(config){
43335     
43336     
43337     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43338     
43339     
43340     this.addEvents({
43341         /**
43342          * @event initialize
43343          * Fires when the editor is fully initialized (including the iframe)
43344          * @param {Roo.HtmlEditorCore} this
43345          */
43346         initialize: true,
43347         /**
43348          * @event activate
43349          * Fires when the editor is first receives the focus. Any insertion must wait
43350          * until after this event.
43351          * @param {Roo.HtmlEditorCore} this
43352          */
43353         activate: true,
43354          /**
43355          * @event beforesync
43356          * Fires before the textarea is updated with content from the editor iframe. Return false
43357          * to cancel the sync.
43358          * @param {Roo.HtmlEditorCore} this
43359          * @param {String} html
43360          */
43361         beforesync: true,
43362          /**
43363          * @event beforepush
43364          * Fires before the iframe editor is updated with content from the textarea. Return false
43365          * to cancel the push.
43366          * @param {Roo.HtmlEditorCore} this
43367          * @param {String} html
43368          */
43369         beforepush: true,
43370          /**
43371          * @event sync
43372          * Fires when the textarea is updated with content from the editor iframe.
43373          * @param {Roo.HtmlEditorCore} this
43374          * @param {String} html
43375          */
43376         sync: true,
43377          /**
43378          * @event push
43379          * Fires when the iframe editor is updated with content from the textarea.
43380          * @param {Roo.HtmlEditorCore} this
43381          * @param {String} html
43382          */
43383         push: true,
43384         
43385         /**
43386          * @event editorevent
43387          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43388          * @param {Roo.HtmlEditorCore} this
43389          */
43390         editorevent: true
43391         
43392     });
43393     
43394     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43395     
43396     // defaults : white / black...
43397     this.applyBlacklists();
43398     
43399     
43400     
43401 };
43402
43403
43404 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43405
43406
43407      /**
43408      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43409      */
43410     
43411     owner : false,
43412     
43413      /**
43414      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43415      *                        Roo.resizable.
43416      */
43417     resizable : false,
43418      /**
43419      * @cfg {Number} height (in pixels)
43420      */   
43421     height: 300,
43422    /**
43423      * @cfg {Number} width (in pixels)
43424      */   
43425     width: 500,
43426     
43427     /**
43428      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43429      * 
43430      */
43431     stylesheets: false,
43432     
43433     // id of frame..
43434     frameId: false,
43435     
43436     // private properties
43437     validationEvent : false,
43438     deferHeight: true,
43439     initialized : false,
43440     activated : false,
43441     sourceEditMode : false,
43442     onFocus : Roo.emptyFn,
43443     iframePad:3,
43444     hideMode:'offsets',
43445     
43446     clearUp: true,
43447     
43448     // blacklist + whitelisted elements..
43449     black: false,
43450     white: false,
43451      
43452     bodyCls : '',
43453
43454     /**
43455      * Protected method that will not generally be called directly. It
43456      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43457      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43458      */
43459     getDocMarkup : function(){
43460         // body styles..
43461         var st = '';
43462         
43463         // inherit styels from page...?? 
43464         if (this.stylesheets === false) {
43465             
43466             Roo.get(document.head).select('style').each(function(node) {
43467                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43468             });
43469             
43470             Roo.get(document.head).select('link').each(function(node) { 
43471                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43472             });
43473             
43474         } else if (!this.stylesheets.length) {
43475                 // simple..
43476                 st = '<style type="text/css">' +
43477                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43478                    '</style>';
43479         } else { 
43480             st = '<style type="text/css">' +
43481                     this.stylesheets +
43482                 '</style>';
43483         }
43484         
43485         st +=  '<style type="text/css">' +
43486             'IMG { cursor: pointer } ' +
43487         '</style>';
43488
43489         var cls = 'roo-htmleditor-body';
43490         
43491         if(this.bodyCls.length){
43492             cls += ' ' + this.bodyCls;
43493         }
43494         
43495         return '<html><head>' + st  +
43496             //<style type="text/css">' +
43497             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43498             //'</style>' +
43499             ' </head><body class="' +  cls + '"></body></html>';
43500     },
43501
43502     // private
43503     onRender : function(ct, position)
43504     {
43505         var _t = this;
43506         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43507         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43508         
43509         
43510         this.el.dom.style.border = '0 none';
43511         this.el.dom.setAttribute('tabIndex', -1);
43512         this.el.addClass('x-hidden hide');
43513         
43514         
43515         
43516         if(Roo.isIE){ // fix IE 1px bogus margin
43517             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43518         }
43519        
43520         
43521         this.frameId = Roo.id();
43522         
43523          
43524         
43525         var iframe = this.owner.wrap.createChild({
43526             tag: 'iframe',
43527             cls: 'form-control', // bootstrap..
43528             id: this.frameId,
43529             name: this.frameId,
43530             frameBorder : 'no',
43531             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43532         }, this.el
43533         );
43534         
43535         
43536         this.iframe = iframe.dom;
43537
43538          this.assignDocWin();
43539         
43540         this.doc.designMode = 'on';
43541        
43542         this.doc.open();
43543         this.doc.write(this.getDocMarkup());
43544         this.doc.close();
43545
43546         
43547         var task = { // must defer to wait for browser to be ready
43548             run : function(){
43549                 //console.log("run task?" + this.doc.readyState);
43550                 this.assignDocWin();
43551                 if(this.doc.body || this.doc.readyState == 'complete'){
43552                     try {
43553                         this.doc.designMode="on";
43554                     } catch (e) {
43555                         return;
43556                     }
43557                     Roo.TaskMgr.stop(task);
43558                     this.initEditor.defer(10, this);
43559                 }
43560             },
43561             interval : 10,
43562             duration: 10000,
43563             scope: this
43564         };
43565         Roo.TaskMgr.start(task);
43566
43567     },
43568
43569     // private
43570     onResize : function(w, h)
43571     {
43572          Roo.log('resize: ' +w + ',' + h );
43573         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43574         if(!this.iframe){
43575             return;
43576         }
43577         if(typeof w == 'number'){
43578             
43579             this.iframe.style.width = w + 'px';
43580         }
43581         if(typeof h == 'number'){
43582             
43583             this.iframe.style.height = h + 'px';
43584             if(this.doc){
43585                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43586             }
43587         }
43588         
43589     },
43590
43591     /**
43592      * Toggles the editor between standard and source edit mode.
43593      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43594      */
43595     toggleSourceEdit : function(sourceEditMode){
43596         
43597         this.sourceEditMode = sourceEditMode === true;
43598         
43599         if(this.sourceEditMode){
43600  
43601             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43602             
43603         }else{
43604             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43605             //this.iframe.className = '';
43606             this.deferFocus();
43607         }
43608         //this.setSize(this.owner.wrap.getSize());
43609         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43610     },
43611
43612     
43613   
43614
43615     /**
43616      * Protected method that will not generally be called directly. If you need/want
43617      * custom HTML cleanup, this is the method you should override.
43618      * @param {String} html The HTML to be cleaned
43619      * return {String} The cleaned HTML
43620      */
43621     cleanHtml : function(html){
43622         html = String(html);
43623         if(html.length > 5){
43624             if(Roo.isSafari){ // strip safari nonsense
43625                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43626             }
43627         }
43628         if(html == '&nbsp;'){
43629             html = '';
43630         }
43631         return html;
43632     },
43633
43634     /**
43635      * HTML Editor -> Textarea
43636      * Protected method that will not generally be called directly. Syncs the contents
43637      * of the editor iframe with the textarea.
43638      */
43639     syncValue : function(){
43640         if(this.initialized){
43641             var bd = (this.doc.body || this.doc.documentElement);
43642             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43643             var html = bd.innerHTML;
43644             if(Roo.isSafari){
43645                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43646                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43647                 if(m && m[1]){
43648                     html = '<div style="'+m[0]+'">' + html + '</div>';
43649                 }
43650             }
43651             html = this.cleanHtml(html);
43652             // fix up the special chars.. normaly like back quotes in word...
43653             // however we do not want to do this with chinese..
43654             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43655                 
43656                 var cc = match.charCodeAt();
43657
43658                 // Get the character value, handling surrogate pairs
43659                 if (match.length == 2) {
43660                     // It's a surrogate pair, calculate the Unicode code point
43661                     var high = match.charCodeAt(0) - 0xD800;
43662                     var low  = match.charCodeAt(1) - 0xDC00;
43663                     cc = (high * 0x400) + low + 0x10000;
43664                 }  else if (
43665                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43666                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43667                     (cc >= 0xf900 && cc < 0xfb00 )
43668                 ) {
43669                         return match;
43670                 }  
43671          
43672                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43673                 return "&#" + cc + ";";
43674                 
43675                 
43676             });
43677             
43678             
43679              
43680             if(this.owner.fireEvent('beforesync', this, html) !== false){
43681                 this.el.dom.value = html;
43682                 this.owner.fireEvent('sync', this, html);
43683             }
43684         }
43685     },
43686
43687     /**
43688      * Protected method that will not generally be called directly. Pushes the value of the textarea
43689      * into the iframe editor.
43690      */
43691     pushValue : function(){
43692         if(this.initialized){
43693             var v = this.el.dom.value.trim();
43694             
43695 //            if(v.length < 1){
43696 //                v = '&#160;';
43697 //            }
43698             
43699             if(this.owner.fireEvent('beforepush', this, v) !== false){
43700                 var d = (this.doc.body || this.doc.documentElement);
43701                 d.innerHTML = v;
43702                 this.cleanUpPaste();
43703                 this.el.dom.value = d.innerHTML;
43704                 this.owner.fireEvent('push', this, v);
43705             }
43706         }
43707     },
43708
43709     // private
43710     deferFocus : function(){
43711         this.focus.defer(10, this);
43712     },
43713
43714     // doc'ed in Field
43715     focus : function(){
43716         if(this.win && !this.sourceEditMode){
43717             this.win.focus();
43718         }else{
43719             this.el.focus();
43720         }
43721     },
43722     
43723     assignDocWin: function()
43724     {
43725         var iframe = this.iframe;
43726         
43727          if(Roo.isIE){
43728             this.doc = iframe.contentWindow.document;
43729             this.win = iframe.contentWindow;
43730         } else {
43731 //            if (!Roo.get(this.frameId)) {
43732 //                return;
43733 //            }
43734 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43735 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43736             
43737             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43738                 return;
43739             }
43740             
43741             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43742             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43743         }
43744     },
43745     
43746     // private
43747     initEditor : function(){
43748         //console.log("INIT EDITOR");
43749         this.assignDocWin();
43750         
43751         
43752         
43753         this.doc.designMode="on";
43754         this.doc.open();
43755         this.doc.write(this.getDocMarkup());
43756         this.doc.close();
43757         
43758         var dbody = (this.doc.body || this.doc.documentElement);
43759         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43760         // this copies styles from the containing element into thsi one..
43761         // not sure why we need all of this..
43762         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43763         
43764         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43765         //ss['background-attachment'] = 'fixed'; // w3c
43766         dbody.bgProperties = 'fixed'; // ie
43767         //Roo.DomHelper.applyStyles(dbody, ss);
43768         Roo.EventManager.on(this.doc, {
43769             //'mousedown': this.onEditorEvent,
43770             'mouseup': this.onEditorEvent,
43771             'dblclick': this.onEditorEvent,
43772             'click': this.onEditorEvent,
43773             'keyup': this.onEditorEvent,
43774             buffer:100,
43775             scope: this
43776         });
43777         if(Roo.isGecko){
43778             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43779         }
43780         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43781             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43782         }
43783         this.initialized = true;
43784
43785         this.owner.fireEvent('initialize', this);
43786         this.pushValue();
43787     },
43788
43789     // private
43790     onDestroy : function(){
43791         
43792         
43793         
43794         if(this.rendered){
43795             
43796             //for (var i =0; i < this.toolbars.length;i++) {
43797             //    // fixme - ask toolbars for heights?
43798             //    this.toolbars[i].onDestroy();
43799            // }
43800             
43801             //this.wrap.dom.innerHTML = '';
43802             //this.wrap.remove();
43803         }
43804     },
43805
43806     // private
43807     onFirstFocus : function(){
43808         
43809         this.assignDocWin();
43810         
43811         
43812         this.activated = true;
43813          
43814     
43815         if(Roo.isGecko){ // prevent silly gecko errors
43816             this.win.focus();
43817             var s = this.win.getSelection();
43818             if(!s.focusNode || s.focusNode.nodeType != 3){
43819                 var r = s.getRangeAt(0);
43820                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43821                 r.collapse(true);
43822                 this.deferFocus();
43823             }
43824             try{
43825                 this.execCmd('useCSS', true);
43826                 this.execCmd('styleWithCSS', false);
43827             }catch(e){}
43828         }
43829         this.owner.fireEvent('activate', this);
43830     },
43831
43832     // private
43833     adjustFont: function(btn){
43834         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43835         //if(Roo.isSafari){ // safari
43836         //    adjust *= 2;
43837        // }
43838         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43839         if(Roo.isSafari){ // safari
43840             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43841             v =  (v < 10) ? 10 : v;
43842             v =  (v > 48) ? 48 : v;
43843             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43844             
43845         }
43846         
43847         
43848         v = Math.max(1, v+adjust);
43849         
43850         this.execCmd('FontSize', v  );
43851     },
43852
43853     onEditorEvent : function(e)
43854     {
43855         this.owner.fireEvent('editorevent', this, e);
43856       //  this.updateToolbar();
43857         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43858     },
43859
43860     insertTag : function(tg)
43861     {
43862         // could be a bit smarter... -> wrap the current selected tRoo..
43863         if (tg.toLowerCase() == 'span' ||
43864             tg.toLowerCase() == 'code' ||
43865             tg.toLowerCase() == 'sup' ||
43866             tg.toLowerCase() == 'sub' 
43867             ) {
43868             
43869             range = this.createRange(this.getSelection());
43870             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43871             wrappingNode.appendChild(range.extractContents());
43872             range.insertNode(wrappingNode);
43873
43874             return;
43875             
43876             
43877             
43878         }
43879         this.execCmd("formatblock",   tg);
43880         
43881     },
43882     
43883     insertText : function(txt)
43884     {
43885         
43886         
43887         var range = this.createRange();
43888         range.deleteContents();
43889                //alert(Sender.getAttribute('label'));
43890                
43891         range.insertNode(this.doc.createTextNode(txt));
43892     } ,
43893     
43894      
43895
43896     /**
43897      * Executes a Midas editor command on the editor document and performs necessary focus and
43898      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43899      * @param {String} cmd The Midas command
43900      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43901      */
43902     relayCmd : function(cmd, value){
43903         this.win.focus();
43904         this.execCmd(cmd, value);
43905         this.owner.fireEvent('editorevent', this);
43906         //this.updateToolbar();
43907         this.owner.deferFocus();
43908     },
43909
43910     /**
43911      * Executes a Midas editor command directly on the editor document.
43912      * For visual commands, you should use {@link #relayCmd} instead.
43913      * <b>This should only be called after the editor is initialized.</b>
43914      * @param {String} cmd The Midas command
43915      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43916      */
43917     execCmd : function(cmd, value){
43918         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43919         this.syncValue();
43920     },
43921  
43922  
43923    
43924     /**
43925      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43926      * to insert tRoo.
43927      * @param {String} text | dom node.. 
43928      */
43929     insertAtCursor : function(text)
43930     {
43931         
43932         if(!this.activated){
43933             return;
43934         }
43935         /*
43936         if(Roo.isIE){
43937             this.win.focus();
43938             var r = this.doc.selection.createRange();
43939             if(r){
43940                 r.collapse(true);
43941                 r.pasteHTML(text);
43942                 this.syncValue();
43943                 this.deferFocus();
43944             
43945             }
43946             return;
43947         }
43948         */
43949         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43950             this.win.focus();
43951             
43952             
43953             // from jquery ui (MIT licenced)
43954             var range, node;
43955             var win = this.win;
43956             
43957             if (win.getSelection && win.getSelection().getRangeAt) {
43958                 range = win.getSelection().getRangeAt(0);
43959                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43960                 range.insertNode(node);
43961             } else if (win.document.selection && win.document.selection.createRange) {
43962                 // no firefox support
43963                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43964                 win.document.selection.createRange().pasteHTML(txt);
43965             } else {
43966                 // no firefox support
43967                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43968                 this.execCmd('InsertHTML', txt);
43969             } 
43970             
43971             this.syncValue();
43972             
43973             this.deferFocus();
43974         }
43975     },
43976  // private
43977     mozKeyPress : function(e){
43978         if(e.ctrlKey){
43979             var c = e.getCharCode(), cmd;
43980           
43981             if(c > 0){
43982                 c = String.fromCharCode(c).toLowerCase();
43983                 switch(c){
43984                     case 'b':
43985                         cmd = 'bold';
43986                         break;
43987                     case 'i':
43988                         cmd = 'italic';
43989                         break;
43990                     
43991                     case 'u':
43992                         cmd = 'underline';
43993                         break;
43994                     
43995                     case 'v':
43996                         this.cleanUpPaste.defer(100, this);
43997                         return;
43998                         
43999                 }
44000                 if(cmd){
44001                     this.win.focus();
44002                     this.execCmd(cmd);
44003                     this.deferFocus();
44004                     e.preventDefault();
44005                 }
44006                 
44007             }
44008         }
44009     },
44010
44011     // private
44012     fixKeys : function(){ // load time branching for fastest keydown performance
44013         if(Roo.isIE){
44014             return function(e){
44015                 var k = e.getKey(), r;
44016                 if(k == e.TAB){
44017                     e.stopEvent();
44018                     r = this.doc.selection.createRange();
44019                     if(r){
44020                         r.collapse(true);
44021                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44022                         this.deferFocus();
44023                     }
44024                     return;
44025                 }
44026                 
44027                 if(k == e.ENTER){
44028                     r = this.doc.selection.createRange();
44029                     if(r){
44030                         var target = r.parentElement();
44031                         if(!target || target.tagName.toLowerCase() != 'li'){
44032                             e.stopEvent();
44033                             r.pasteHTML('<br />');
44034                             r.collapse(false);
44035                             r.select();
44036                         }
44037                     }
44038                 }
44039                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44040                     this.cleanUpPaste.defer(100, this);
44041                     return;
44042                 }
44043                 
44044                 
44045             };
44046         }else if(Roo.isOpera){
44047             return function(e){
44048                 var k = e.getKey();
44049                 if(k == e.TAB){
44050                     e.stopEvent();
44051                     this.win.focus();
44052                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44053                     this.deferFocus();
44054                 }
44055                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44056                     this.cleanUpPaste.defer(100, this);
44057                     return;
44058                 }
44059                 
44060             };
44061         }else if(Roo.isSafari){
44062             return function(e){
44063                 var k = e.getKey();
44064                 
44065                 if(k == e.TAB){
44066                     e.stopEvent();
44067                     this.execCmd('InsertText','\t');
44068                     this.deferFocus();
44069                     return;
44070                 }
44071                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44072                     this.cleanUpPaste.defer(100, this);
44073                     return;
44074                 }
44075                 
44076              };
44077         }
44078     }(),
44079     
44080     getAllAncestors: function()
44081     {
44082         var p = this.getSelectedNode();
44083         var a = [];
44084         if (!p) {
44085             a.push(p); // push blank onto stack..
44086             p = this.getParentElement();
44087         }
44088         
44089         
44090         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44091             a.push(p);
44092             p = p.parentNode;
44093         }
44094         a.push(this.doc.body);
44095         return a;
44096     },
44097     lastSel : false,
44098     lastSelNode : false,
44099     
44100     
44101     getSelection : function() 
44102     {
44103         this.assignDocWin();
44104         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44105     },
44106     
44107     getSelectedNode: function() 
44108     {
44109         // this may only work on Gecko!!!
44110         
44111         // should we cache this!!!!
44112         
44113         
44114         
44115          
44116         var range = this.createRange(this.getSelection()).cloneRange();
44117         
44118         if (Roo.isIE) {
44119             var parent = range.parentElement();
44120             while (true) {
44121                 var testRange = range.duplicate();
44122                 testRange.moveToElementText(parent);
44123                 if (testRange.inRange(range)) {
44124                     break;
44125                 }
44126                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44127                     break;
44128                 }
44129                 parent = parent.parentElement;
44130             }
44131             return parent;
44132         }
44133         
44134         // is ancestor a text element.
44135         var ac =  range.commonAncestorContainer;
44136         if (ac.nodeType == 3) {
44137             ac = ac.parentNode;
44138         }
44139         
44140         var ar = ac.childNodes;
44141          
44142         var nodes = [];
44143         var other_nodes = [];
44144         var has_other_nodes = false;
44145         for (var i=0;i<ar.length;i++) {
44146             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44147                 continue;
44148             }
44149             // fullly contained node.
44150             
44151             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44152                 nodes.push(ar[i]);
44153                 continue;
44154             }
44155             
44156             // probably selected..
44157             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44158                 other_nodes.push(ar[i]);
44159                 continue;
44160             }
44161             // outer..
44162             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44163                 continue;
44164             }
44165             
44166             
44167             has_other_nodes = true;
44168         }
44169         if (!nodes.length && other_nodes.length) {
44170             nodes= other_nodes;
44171         }
44172         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44173             return false;
44174         }
44175         
44176         return nodes[0];
44177     },
44178     createRange: function(sel)
44179     {
44180         // this has strange effects when using with 
44181         // top toolbar - not sure if it's a great idea.
44182         //this.editor.contentWindow.focus();
44183         if (typeof sel != "undefined") {
44184             try {
44185                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44186             } catch(e) {
44187                 return this.doc.createRange();
44188             }
44189         } else {
44190             return this.doc.createRange();
44191         }
44192     },
44193     getParentElement: function()
44194     {
44195         
44196         this.assignDocWin();
44197         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44198         
44199         var range = this.createRange(sel);
44200          
44201         try {
44202             var p = range.commonAncestorContainer;
44203             while (p.nodeType == 3) { // text node
44204                 p = p.parentNode;
44205             }
44206             return p;
44207         } catch (e) {
44208             return null;
44209         }
44210     
44211     },
44212     /***
44213      *
44214      * Range intersection.. the hard stuff...
44215      *  '-1' = before
44216      *  '0' = hits..
44217      *  '1' = after.
44218      *         [ -- selected range --- ]
44219      *   [fail]                        [fail]
44220      *
44221      *    basically..
44222      *      if end is before start or  hits it. fail.
44223      *      if start is after end or hits it fail.
44224      *
44225      *   if either hits (but other is outside. - then it's not 
44226      *   
44227      *    
44228      **/
44229     
44230     
44231     // @see http://www.thismuchiknow.co.uk/?p=64.
44232     rangeIntersectsNode : function(range, node)
44233     {
44234         var nodeRange = node.ownerDocument.createRange();
44235         try {
44236             nodeRange.selectNode(node);
44237         } catch (e) {
44238             nodeRange.selectNodeContents(node);
44239         }
44240     
44241         var rangeStartRange = range.cloneRange();
44242         rangeStartRange.collapse(true);
44243     
44244         var rangeEndRange = range.cloneRange();
44245         rangeEndRange.collapse(false);
44246     
44247         var nodeStartRange = nodeRange.cloneRange();
44248         nodeStartRange.collapse(true);
44249     
44250         var nodeEndRange = nodeRange.cloneRange();
44251         nodeEndRange.collapse(false);
44252     
44253         return rangeStartRange.compareBoundaryPoints(
44254                  Range.START_TO_START, nodeEndRange) == -1 &&
44255                rangeEndRange.compareBoundaryPoints(
44256                  Range.START_TO_START, nodeStartRange) == 1;
44257         
44258          
44259     },
44260     rangeCompareNode : function(range, node)
44261     {
44262         var nodeRange = node.ownerDocument.createRange();
44263         try {
44264             nodeRange.selectNode(node);
44265         } catch (e) {
44266             nodeRange.selectNodeContents(node);
44267         }
44268         
44269         
44270         range.collapse(true);
44271     
44272         nodeRange.collapse(true);
44273      
44274         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44275         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44276          
44277         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44278         
44279         var nodeIsBefore   =  ss == 1;
44280         var nodeIsAfter    = ee == -1;
44281         
44282         if (nodeIsBefore && nodeIsAfter) {
44283             return 0; // outer
44284         }
44285         if (!nodeIsBefore && nodeIsAfter) {
44286             return 1; //right trailed.
44287         }
44288         
44289         if (nodeIsBefore && !nodeIsAfter) {
44290             return 2;  // left trailed.
44291         }
44292         // fully contined.
44293         return 3;
44294     },
44295
44296     // private? - in a new class?
44297     cleanUpPaste :  function()
44298     {
44299         // cleans up the whole document..
44300         Roo.log('cleanuppaste');
44301         
44302         this.cleanUpChildren(this.doc.body);
44303         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44304         if (clean != this.doc.body.innerHTML) {
44305             this.doc.body.innerHTML = clean;
44306         }
44307         
44308     },
44309     
44310     cleanWordChars : function(input) {// change the chars to hex code
44311         var he = Roo.HtmlEditorCore;
44312         
44313         var output = input;
44314         Roo.each(he.swapCodes, function(sw) { 
44315             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44316             
44317             output = output.replace(swapper, sw[1]);
44318         });
44319         
44320         return output;
44321     },
44322     
44323     
44324     cleanUpChildren : function (n)
44325     {
44326         if (!n.childNodes.length) {
44327             return;
44328         }
44329         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44330            this.cleanUpChild(n.childNodes[i]);
44331         }
44332     },
44333     
44334     
44335         
44336     
44337     cleanUpChild : function (node)
44338     {
44339         var ed = this;
44340         //console.log(node);
44341         if (node.nodeName == "#text") {
44342             // clean up silly Windows -- stuff?
44343             return; 
44344         }
44345         if (node.nodeName == "#comment") {
44346             node.parentNode.removeChild(node);
44347             // clean up silly Windows -- stuff?
44348             return; 
44349         }
44350         var lcname = node.tagName.toLowerCase();
44351         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44352         // whitelist of tags..
44353         
44354         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44355             // remove node.
44356             node.parentNode.removeChild(node);
44357             return;
44358             
44359         }
44360         
44361         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44362         
44363         // spans with no attributes - just remove them..
44364         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44365             remove_keep_children = true;
44366         }
44367         
44368         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44369         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44370         
44371         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44372         //    remove_keep_children = true;
44373         //}
44374         
44375         if (remove_keep_children) {
44376             this.cleanUpChildren(node);
44377             // inserts everything just before this node...
44378             while (node.childNodes.length) {
44379                 var cn = node.childNodes[0];
44380                 node.removeChild(cn);
44381                 node.parentNode.insertBefore(cn, node);
44382             }
44383             node.parentNode.removeChild(node);
44384             return;
44385         }
44386         
44387         if (!node.attributes || !node.attributes.length) {
44388             
44389           
44390             
44391             
44392             this.cleanUpChildren(node);
44393             return;
44394         }
44395         
44396         function cleanAttr(n,v)
44397         {
44398             
44399             if (v.match(/^\./) || v.match(/^\//)) {
44400                 return;
44401             }
44402             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44403                 return;
44404             }
44405             if (v.match(/^#/)) {
44406                 return;
44407             }
44408 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44409             node.removeAttribute(n);
44410             
44411         }
44412         
44413         var cwhite = this.cwhite;
44414         var cblack = this.cblack;
44415             
44416         function cleanStyle(n,v)
44417         {
44418             if (v.match(/expression/)) { //XSS?? should we even bother..
44419                 node.removeAttribute(n);
44420                 return;
44421             }
44422             
44423             var parts = v.split(/;/);
44424             var clean = [];
44425             
44426             Roo.each(parts, function(p) {
44427                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44428                 if (!p.length) {
44429                     return true;
44430                 }
44431                 var l = p.split(':').shift().replace(/\s+/g,'');
44432                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44433                 
44434                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44435 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44436                     //node.removeAttribute(n);
44437                     return true;
44438                 }
44439                 //Roo.log()
44440                 // only allow 'c whitelisted system attributes'
44441                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44442 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44443                     //node.removeAttribute(n);
44444                     return true;
44445                 }
44446                 
44447                 
44448                  
44449                 
44450                 clean.push(p);
44451                 return true;
44452             });
44453             if (clean.length) { 
44454                 node.setAttribute(n, clean.join(';'));
44455             } else {
44456                 node.removeAttribute(n);
44457             }
44458             
44459         }
44460         
44461         
44462         for (var i = node.attributes.length-1; i > -1 ; i--) {
44463             var a = node.attributes[i];
44464             //console.log(a);
44465             
44466             if (a.name.toLowerCase().substr(0,2)=='on')  {
44467                 node.removeAttribute(a.name);
44468                 continue;
44469             }
44470             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44471                 node.removeAttribute(a.name);
44472                 continue;
44473             }
44474             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44475                 cleanAttr(a.name,a.value); // fixme..
44476                 continue;
44477             }
44478             if (a.name == 'style') {
44479                 cleanStyle(a.name,a.value);
44480                 continue;
44481             }
44482             /// clean up MS crap..
44483             // tecnically this should be a list of valid class'es..
44484             
44485             
44486             if (a.name == 'class') {
44487                 if (a.value.match(/^Mso/)) {
44488                     node.removeAttribute('class');
44489                 }
44490                 
44491                 if (a.value.match(/^body$/)) {
44492                     node.removeAttribute('class');
44493                 }
44494                 continue;
44495             }
44496             
44497             // style cleanup!?
44498             // class cleanup?
44499             
44500         }
44501         
44502         
44503         this.cleanUpChildren(node);
44504         
44505         
44506     },
44507     
44508     /**
44509      * Clean up MS wordisms...
44510      */
44511     cleanWord : function(node)
44512     {
44513         if (!node) {
44514             this.cleanWord(this.doc.body);
44515             return;
44516         }
44517         
44518         if(
44519                 node.nodeName == 'SPAN' &&
44520                 !node.hasAttributes() &&
44521                 node.childNodes.length == 1 &&
44522                 node.firstChild.nodeName == "#text"  
44523         ) {
44524             var textNode = node.firstChild;
44525             node.removeChild(textNode);
44526             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44527                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44528             }
44529             node.parentNode.insertBefore(textNode, node);
44530             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44531                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44532             }
44533             node.parentNode.removeChild(node);
44534         }
44535         
44536         if (node.nodeName == "#text") {
44537             // clean up silly Windows -- stuff?
44538             return; 
44539         }
44540         if (node.nodeName == "#comment") {
44541             node.parentNode.removeChild(node);
44542             // clean up silly Windows -- stuff?
44543             return; 
44544         }
44545         
44546         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44547             node.parentNode.removeChild(node);
44548             return;
44549         }
44550         //Roo.log(node.tagName);
44551         // remove - but keep children..
44552         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44553             //Roo.log('-- removed');
44554             while (node.childNodes.length) {
44555                 var cn = node.childNodes[0];
44556                 node.removeChild(cn);
44557                 node.parentNode.insertBefore(cn, node);
44558                 // move node to parent - and clean it..
44559                 this.cleanWord(cn);
44560             }
44561             node.parentNode.removeChild(node);
44562             /// no need to iterate chidlren = it's got none..
44563             //this.iterateChildren(node, this.cleanWord);
44564             return;
44565         }
44566         // clean styles
44567         if (node.className.length) {
44568             
44569             var cn = node.className.split(/\W+/);
44570             var cna = [];
44571             Roo.each(cn, function(cls) {
44572                 if (cls.match(/Mso[a-zA-Z]+/)) {
44573                     return;
44574                 }
44575                 cna.push(cls);
44576             });
44577             node.className = cna.length ? cna.join(' ') : '';
44578             if (!cna.length) {
44579                 node.removeAttribute("class");
44580             }
44581         }
44582         
44583         if (node.hasAttribute("lang")) {
44584             node.removeAttribute("lang");
44585         }
44586         
44587         if (node.hasAttribute("style")) {
44588             
44589             var styles = node.getAttribute("style").split(";");
44590             var nstyle = [];
44591             Roo.each(styles, function(s) {
44592                 if (!s.match(/:/)) {
44593                     return;
44594                 }
44595                 var kv = s.split(":");
44596                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44597                     return;
44598                 }
44599                 // what ever is left... we allow.
44600                 nstyle.push(s);
44601             });
44602             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44603             if (!nstyle.length) {
44604                 node.removeAttribute('style');
44605             }
44606         }
44607         this.iterateChildren(node, this.cleanWord);
44608         
44609         
44610         
44611     },
44612     /**
44613      * iterateChildren of a Node, calling fn each time, using this as the scole..
44614      * @param {DomNode} node node to iterate children of.
44615      * @param {Function} fn method of this class to call on each item.
44616      */
44617     iterateChildren : function(node, fn)
44618     {
44619         if (!node.childNodes.length) {
44620                 return;
44621         }
44622         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44623            fn.call(this, node.childNodes[i])
44624         }
44625     },
44626     
44627     
44628     /**
44629      * cleanTableWidths.
44630      *
44631      * Quite often pasting from word etc.. results in tables with column and widths.
44632      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44633      *
44634      */
44635     cleanTableWidths : function(node)
44636     {
44637          
44638          
44639         if (!node) {
44640             this.cleanTableWidths(this.doc.body);
44641             return;
44642         }
44643         
44644         // ignore list...
44645         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44646             return; 
44647         }
44648         Roo.log(node.tagName);
44649         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44650             this.iterateChildren(node, this.cleanTableWidths);
44651             return;
44652         }
44653         if (node.hasAttribute('width')) {
44654             node.removeAttribute('width');
44655         }
44656         
44657          
44658         if (node.hasAttribute("style")) {
44659             // pretty basic...
44660             
44661             var styles = node.getAttribute("style").split(";");
44662             var nstyle = [];
44663             Roo.each(styles, function(s) {
44664                 if (!s.match(/:/)) {
44665                     return;
44666                 }
44667                 var kv = s.split(":");
44668                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44669                     return;
44670                 }
44671                 // what ever is left... we allow.
44672                 nstyle.push(s);
44673             });
44674             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44675             if (!nstyle.length) {
44676                 node.removeAttribute('style');
44677             }
44678         }
44679         
44680         this.iterateChildren(node, this.cleanTableWidths);
44681         
44682         
44683     },
44684     
44685     
44686     
44687     
44688     domToHTML : function(currentElement, depth, nopadtext) {
44689         
44690         depth = depth || 0;
44691         nopadtext = nopadtext || false;
44692     
44693         if (!currentElement) {
44694             return this.domToHTML(this.doc.body);
44695         }
44696         
44697         //Roo.log(currentElement);
44698         var j;
44699         var allText = false;
44700         var nodeName = currentElement.nodeName;
44701         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44702         
44703         if  (nodeName == '#text') {
44704             
44705             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44706         }
44707         
44708         
44709         var ret = '';
44710         if (nodeName != 'BODY') {
44711              
44712             var i = 0;
44713             // Prints the node tagName, such as <A>, <IMG>, etc
44714             if (tagName) {
44715                 var attr = [];
44716                 for(i = 0; i < currentElement.attributes.length;i++) {
44717                     // quoting?
44718                     var aname = currentElement.attributes.item(i).name;
44719                     if (!currentElement.attributes.item(i).value.length) {
44720                         continue;
44721                     }
44722                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44723                 }
44724                 
44725                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44726             } 
44727             else {
44728                 
44729                 // eack
44730             }
44731         } else {
44732             tagName = false;
44733         }
44734         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44735             return ret;
44736         }
44737         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44738             nopadtext = true;
44739         }
44740         
44741         
44742         // Traverse the tree
44743         i = 0;
44744         var currentElementChild = currentElement.childNodes.item(i);
44745         var allText = true;
44746         var innerHTML  = '';
44747         lastnode = '';
44748         while (currentElementChild) {
44749             // Formatting code (indent the tree so it looks nice on the screen)
44750             var nopad = nopadtext;
44751             if (lastnode == 'SPAN') {
44752                 nopad  = true;
44753             }
44754             // text
44755             if  (currentElementChild.nodeName == '#text') {
44756                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44757                 toadd = nopadtext ? toadd : toadd.trim();
44758                 if (!nopad && toadd.length > 80) {
44759                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44760                 }
44761                 innerHTML  += toadd;
44762                 
44763                 i++;
44764                 currentElementChild = currentElement.childNodes.item(i);
44765                 lastNode = '';
44766                 continue;
44767             }
44768             allText = false;
44769             
44770             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44771                 
44772             // Recursively traverse the tree structure of the child node
44773             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44774             lastnode = currentElementChild.nodeName;
44775             i++;
44776             currentElementChild=currentElement.childNodes.item(i);
44777         }
44778         
44779         ret += innerHTML;
44780         
44781         if (!allText) {
44782                 // The remaining code is mostly for formatting the tree
44783             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44784         }
44785         
44786         
44787         if (tagName) {
44788             ret+= "</"+tagName+">";
44789         }
44790         return ret;
44791         
44792     },
44793         
44794     applyBlacklists : function()
44795     {
44796         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44797         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44798         
44799         this.white = [];
44800         this.black = [];
44801         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44802             if (b.indexOf(tag) > -1) {
44803                 return;
44804             }
44805             this.white.push(tag);
44806             
44807         }, this);
44808         
44809         Roo.each(w, function(tag) {
44810             if (b.indexOf(tag) > -1) {
44811                 return;
44812             }
44813             if (this.white.indexOf(tag) > -1) {
44814                 return;
44815             }
44816             this.white.push(tag);
44817             
44818         }, this);
44819         
44820         
44821         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44822             if (w.indexOf(tag) > -1) {
44823                 return;
44824             }
44825             this.black.push(tag);
44826             
44827         }, this);
44828         
44829         Roo.each(b, function(tag) {
44830             if (w.indexOf(tag) > -1) {
44831                 return;
44832             }
44833             if (this.black.indexOf(tag) > -1) {
44834                 return;
44835             }
44836             this.black.push(tag);
44837             
44838         }, this);
44839         
44840         
44841         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44842         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44843         
44844         this.cwhite = [];
44845         this.cblack = [];
44846         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44847             if (b.indexOf(tag) > -1) {
44848                 return;
44849             }
44850             this.cwhite.push(tag);
44851             
44852         }, this);
44853         
44854         Roo.each(w, function(tag) {
44855             if (b.indexOf(tag) > -1) {
44856                 return;
44857             }
44858             if (this.cwhite.indexOf(tag) > -1) {
44859                 return;
44860             }
44861             this.cwhite.push(tag);
44862             
44863         }, this);
44864         
44865         
44866         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44867             if (w.indexOf(tag) > -1) {
44868                 return;
44869             }
44870             this.cblack.push(tag);
44871             
44872         }, this);
44873         
44874         Roo.each(b, function(tag) {
44875             if (w.indexOf(tag) > -1) {
44876                 return;
44877             }
44878             if (this.cblack.indexOf(tag) > -1) {
44879                 return;
44880             }
44881             this.cblack.push(tag);
44882             
44883         }, this);
44884     },
44885     
44886     setStylesheets : function(stylesheets)
44887     {
44888         if(typeof(stylesheets) == 'string'){
44889             Roo.get(this.iframe.contentDocument.head).createChild({
44890                 tag : 'link',
44891                 rel : 'stylesheet',
44892                 type : 'text/css',
44893                 href : stylesheets
44894             });
44895             
44896             return;
44897         }
44898         var _this = this;
44899      
44900         Roo.each(stylesheets, function(s) {
44901             if(!s.length){
44902                 return;
44903             }
44904             
44905             Roo.get(_this.iframe.contentDocument.head).createChild({
44906                 tag : 'link',
44907                 rel : 'stylesheet',
44908                 type : 'text/css',
44909                 href : s
44910             });
44911         });
44912
44913         
44914     },
44915     
44916     removeStylesheets : function()
44917     {
44918         var _this = this;
44919         
44920         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44921             s.remove();
44922         });
44923     },
44924     
44925     setStyle : function(style)
44926     {
44927         Roo.get(this.iframe.contentDocument.head).createChild({
44928             tag : 'style',
44929             type : 'text/css',
44930             html : style
44931         });
44932
44933         return;
44934     }
44935     
44936     // hide stuff that is not compatible
44937     /**
44938      * @event blur
44939      * @hide
44940      */
44941     /**
44942      * @event change
44943      * @hide
44944      */
44945     /**
44946      * @event focus
44947      * @hide
44948      */
44949     /**
44950      * @event specialkey
44951      * @hide
44952      */
44953     /**
44954      * @cfg {String} fieldClass @hide
44955      */
44956     /**
44957      * @cfg {String} focusClass @hide
44958      */
44959     /**
44960      * @cfg {String} autoCreate @hide
44961      */
44962     /**
44963      * @cfg {String} inputType @hide
44964      */
44965     /**
44966      * @cfg {String} invalidClass @hide
44967      */
44968     /**
44969      * @cfg {String} invalidText @hide
44970      */
44971     /**
44972      * @cfg {String} msgFx @hide
44973      */
44974     /**
44975      * @cfg {String} validateOnBlur @hide
44976      */
44977 });
44978
44979 Roo.HtmlEditorCore.white = [
44980         'area', 'br', 'img', 'input', 'hr', 'wbr',
44981         
44982        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44983        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44984        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44985        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44986        'table',   'ul',         'xmp', 
44987        
44988        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44989       'thead',   'tr', 
44990      
44991       'dir', 'menu', 'ol', 'ul', 'dl',
44992        
44993       'embed',  'object'
44994 ];
44995
44996
44997 Roo.HtmlEditorCore.black = [
44998     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44999         'applet', // 
45000         'base',   'basefont', 'bgsound', 'blink',  'body', 
45001         'frame',  'frameset', 'head',    'html',   'ilayer', 
45002         'iframe', 'layer',  'link',     'meta',    'object',   
45003         'script', 'style' ,'title',  'xml' // clean later..
45004 ];
45005 Roo.HtmlEditorCore.clean = [
45006     'script', 'style', 'title', 'xml'
45007 ];
45008 Roo.HtmlEditorCore.remove = [
45009     'font'
45010 ];
45011 // attributes..
45012
45013 Roo.HtmlEditorCore.ablack = [
45014     'on'
45015 ];
45016     
45017 Roo.HtmlEditorCore.aclean = [ 
45018     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45019 ];
45020
45021 // protocols..
45022 Roo.HtmlEditorCore.pwhite= [
45023         'http',  'https',  'mailto'
45024 ];
45025
45026 // white listed style attributes.
45027 Roo.HtmlEditorCore.cwhite= [
45028       //  'text-align', /// default is to allow most things..
45029       
45030          
45031 //        'font-size'//??
45032 ];
45033
45034 // black listed style attributes.
45035 Roo.HtmlEditorCore.cblack= [
45036       //  'font-size' -- this can be set by the project 
45037 ];
45038
45039
45040 Roo.HtmlEditorCore.swapCodes   =[ 
45041     [    8211, "--" ], 
45042     [    8212, "--" ], 
45043     [    8216,  "'" ],  
45044     [    8217, "'" ],  
45045     [    8220, '"' ],  
45046     [    8221, '"' ],  
45047     [    8226, "*" ],  
45048     [    8230, "..." ]
45049 ]; 
45050
45051     //<script type="text/javascript">
45052
45053 /*
45054  * Ext JS Library 1.1.1
45055  * Copyright(c) 2006-2007, Ext JS, LLC.
45056  * Licence LGPL
45057  * 
45058  */
45059  
45060  
45061 Roo.form.HtmlEditor = function(config){
45062     
45063     
45064     
45065     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45066     
45067     if (!this.toolbars) {
45068         this.toolbars = [];
45069     }
45070     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45071     
45072     
45073 };
45074
45075 /**
45076  * @class Roo.form.HtmlEditor
45077  * @extends Roo.form.Field
45078  * Provides a lightweight HTML Editor component.
45079  *
45080  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45081  * 
45082  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45083  * supported by this editor.</b><br/><br/>
45084  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45085  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45086  */
45087 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45088     /**
45089      * @cfg {Boolean} clearUp
45090      */
45091     clearUp : true,
45092       /**
45093      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45094      */
45095     toolbars : false,
45096    
45097      /**
45098      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45099      *                        Roo.resizable.
45100      */
45101     resizable : false,
45102      /**
45103      * @cfg {Number} height (in pixels)
45104      */   
45105     height: 300,
45106    /**
45107      * @cfg {Number} width (in pixels)
45108      */   
45109     width: 500,
45110     
45111     /**
45112      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45113      * 
45114      */
45115     stylesheets: false,
45116     
45117     
45118      /**
45119      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45120      * 
45121      */
45122     cblack: false,
45123     /**
45124      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45125      * 
45126      */
45127     cwhite: false,
45128     
45129      /**
45130      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45131      * 
45132      */
45133     black: false,
45134     /**
45135      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45136      * 
45137      */
45138     white: false,
45139     
45140     // id of frame..
45141     frameId: false,
45142     
45143     // private properties
45144     validationEvent : false,
45145     deferHeight: true,
45146     initialized : false,
45147     activated : false,
45148     
45149     onFocus : Roo.emptyFn,
45150     iframePad:3,
45151     hideMode:'offsets',
45152     
45153     actionMode : 'container', // defaults to hiding it...
45154     
45155     defaultAutoCreate : { // modified by initCompnoent..
45156         tag: "textarea",
45157         style:"width:500px;height:300px;",
45158         autocomplete: "new-password"
45159     },
45160
45161     // private
45162     initComponent : function(){
45163         this.addEvents({
45164             /**
45165              * @event initialize
45166              * Fires when the editor is fully initialized (including the iframe)
45167              * @param {HtmlEditor} this
45168              */
45169             initialize: true,
45170             /**
45171              * @event activate
45172              * Fires when the editor is first receives the focus. Any insertion must wait
45173              * until after this event.
45174              * @param {HtmlEditor} this
45175              */
45176             activate: true,
45177              /**
45178              * @event beforesync
45179              * Fires before the textarea is updated with content from the editor iframe. Return false
45180              * to cancel the sync.
45181              * @param {HtmlEditor} this
45182              * @param {String} html
45183              */
45184             beforesync: true,
45185              /**
45186              * @event beforepush
45187              * Fires before the iframe editor is updated with content from the textarea. Return false
45188              * to cancel the push.
45189              * @param {HtmlEditor} this
45190              * @param {String} html
45191              */
45192             beforepush: true,
45193              /**
45194              * @event sync
45195              * Fires when the textarea is updated with content from the editor iframe.
45196              * @param {HtmlEditor} this
45197              * @param {String} html
45198              */
45199             sync: true,
45200              /**
45201              * @event push
45202              * Fires when the iframe editor is updated with content from the textarea.
45203              * @param {HtmlEditor} this
45204              * @param {String} html
45205              */
45206             push: true,
45207              /**
45208              * @event editmodechange
45209              * Fires when the editor switches edit modes
45210              * @param {HtmlEditor} this
45211              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45212              */
45213             editmodechange: true,
45214             /**
45215              * @event editorevent
45216              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45217              * @param {HtmlEditor} this
45218              */
45219             editorevent: true,
45220             /**
45221              * @event firstfocus
45222              * Fires when on first focus - needed by toolbars..
45223              * @param {HtmlEditor} this
45224              */
45225             firstfocus: true,
45226             /**
45227              * @event autosave
45228              * Auto save the htmlEditor value as a file into Events
45229              * @param {HtmlEditor} this
45230              */
45231             autosave: true,
45232             /**
45233              * @event savedpreview
45234              * preview the saved version of htmlEditor
45235              * @param {HtmlEditor} this
45236              */
45237             savedpreview: true,
45238             
45239             /**
45240             * @event stylesheetsclick
45241             * Fires when press the Sytlesheets button
45242             * @param {Roo.HtmlEditorCore} this
45243             */
45244             stylesheetsclick: true
45245         });
45246         this.defaultAutoCreate =  {
45247             tag: "textarea",
45248             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45249             autocomplete: "new-password"
45250         };
45251     },
45252
45253     /**
45254      * Protected method that will not generally be called directly. It
45255      * is called when the editor creates its toolbar. Override this method if you need to
45256      * add custom toolbar buttons.
45257      * @param {HtmlEditor} editor
45258      */
45259     createToolbar : function(editor){
45260         Roo.log("create toolbars");
45261         if (!editor.toolbars || !editor.toolbars.length) {
45262             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45263         }
45264         
45265         for (var i =0 ; i < editor.toolbars.length;i++) {
45266             editor.toolbars[i] = Roo.factory(
45267                     typeof(editor.toolbars[i]) == 'string' ?
45268                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45269                 Roo.form.HtmlEditor);
45270             editor.toolbars[i].init(editor);
45271         }
45272          
45273         
45274     },
45275
45276      
45277     // private
45278     onRender : function(ct, position)
45279     {
45280         var _t = this;
45281         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45282         
45283         this.wrap = this.el.wrap({
45284             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45285         });
45286         
45287         this.editorcore.onRender(ct, position);
45288          
45289         if (this.resizable) {
45290             this.resizeEl = new Roo.Resizable(this.wrap, {
45291                 pinned : true,
45292                 wrap: true,
45293                 dynamic : true,
45294                 minHeight : this.height,
45295                 height: this.height,
45296                 handles : this.resizable,
45297                 width: this.width,
45298                 listeners : {
45299                     resize : function(r, w, h) {
45300                         _t.onResize(w,h); // -something
45301                     }
45302                 }
45303             });
45304             
45305         }
45306         this.createToolbar(this);
45307        
45308         
45309         if(!this.width){
45310             this.setSize(this.wrap.getSize());
45311         }
45312         if (this.resizeEl) {
45313             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45314             // should trigger onReize..
45315         }
45316         
45317         this.keyNav = new Roo.KeyNav(this.el, {
45318             
45319             "tab" : function(e){
45320                 e.preventDefault();
45321                 
45322                 var value = this.getValue();
45323                 
45324                 var start = this.el.dom.selectionStart;
45325                 var end = this.el.dom.selectionEnd;
45326                 
45327                 if(!e.shiftKey){
45328                     
45329                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45330                     this.el.dom.setSelectionRange(end + 1, end + 1);
45331                     return;
45332                 }
45333                 
45334                 var f = value.substring(0, start).split("\t");
45335                 
45336                 if(f.pop().length != 0){
45337                     return;
45338                 }
45339                 
45340                 this.setValue(f.join("\t") + value.substring(end));
45341                 this.el.dom.setSelectionRange(start - 1, start - 1);
45342                 
45343             },
45344             
45345             "home" : function(e){
45346                 e.preventDefault();
45347                 
45348                 var curr = this.el.dom.selectionStart;
45349                 var lines = this.getValue().split("\n");
45350                 
45351                 if(!lines.length){
45352                     return;
45353                 }
45354                 
45355                 if(e.ctrlKey){
45356                     this.el.dom.setSelectionRange(0, 0);
45357                     return;
45358                 }
45359                 
45360                 var pos = 0;
45361                 
45362                 for (var i = 0; i < lines.length;i++) {
45363                     pos += lines[i].length;
45364                     
45365                     if(i != 0){
45366                         pos += 1;
45367                     }
45368                     
45369                     if(pos < curr){
45370                         continue;
45371                     }
45372                     
45373                     pos -= lines[i].length;
45374                     
45375                     break;
45376                 }
45377                 
45378                 if(!e.shiftKey){
45379                     this.el.dom.setSelectionRange(pos, pos);
45380                     return;
45381                 }
45382                 
45383                 this.el.dom.selectionStart = pos;
45384                 this.el.dom.selectionEnd = curr;
45385             },
45386             
45387             "end" : function(e){
45388                 e.preventDefault();
45389                 
45390                 var curr = this.el.dom.selectionStart;
45391                 var lines = this.getValue().split("\n");
45392                 
45393                 if(!lines.length){
45394                     return;
45395                 }
45396                 
45397                 if(e.ctrlKey){
45398                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45399                     return;
45400                 }
45401                 
45402                 var pos = 0;
45403                 
45404                 for (var i = 0; i < lines.length;i++) {
45405                     
45406                     pos += lines[i].length;
45407                     
45408                     if(i != 0){
45409                         pos += 1;
45410                     }
45411                     
45412                     if(pos < curr){
45413                         continue;
45414                     }
45415                     
45416                     break;
45417                 }
45418                 
45419                 if(!e.shiftKey){
45420                     this.el.dom.setSelectionRange(pos, pos);
45421                     return;
45422                 }
45423                 
45424                 this.el.dom.selectionStart = curr;
45425                 this.el.dom.selectionEnd = pos;
45426             },
45427
45428             scope : this,
45429
45430             doRelay : function(foo, bar, hname){
45431                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45432             },
45433
45434             forceKeyDown: true
45435         });
45436         
45437 //        if(this.autosave && this.w){
45438 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45439 //        }
45440     },
45441
45442     // private
45443     onResize : function(w, h)
45444     {
45445         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45446         var ew = false;
45447         var eh = false;
45448         
45449         if(this.el ){
45450             if(typeof w == 'number'){
45451                 var aw = w - this.wrap.getFrameWidth('lr');
45452                 this.el.setWidth(this.adjustWidth('textarea', aw));
45453                 ew = aw;
45454             }
45455             if(typeof h == 'number'){
45456                 var tbh = 0;
45457                 for (var i =0; i < this.toolbars.length;i++) {
45458                     // fixme - ask toolbars for heights?
45459                     tbh += this.toolbars[i].tb.el.getHeight();
45460                     if (this.toolbars[i].footer) {
45461                         tbh += this.toolbars[i].footer.el.getHeight();
45462                     }
45463                 }
45464                 
45465                 
45466                 
45467                 
45468                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45469                 ah -= 5; // knock a few pixes off for look..
45470 //                Roo.log(ah);
45471                 this.el.setHeight(this.adjustWidth('textarea', ah));
45472                 var eh = ah;
45473             }
45474         }
45475         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45476         this.editorcore.onResize(ew,eh);
45477         
45478     },
45479
45480     /**
45481      * Toggles the editor between standard and source edit mode.
45482      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45483      */
45484     toggleSourceEdit : function(sourceEditMode)
45485     {
45486         this.editorcore.toggleSourceEdit(sourceEditMode);
45487         
45488         if(this.editorcore.sourceEditMode){
45489             Roo.log('editor - showing textarea');
45490             
45491 //            Roo.log('in');
45492 //            Roo.log(this.syncValue());
45493             this.editorcore.syncValue();
45494             this.el.removeClass('x-hidden');
45495             this.el.dom.removeAttribute('tabIndex');
45496             this.el.focus();
45497             
45498             for (var i = 0; i < this.toolbars.length; i++) {
45499                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45500                     this.toolbars[i].tb.hide();
45501                     this.toolbars[i].footer.hide();
45502                 }
45503             }
45504             
45505         }else{
45506             Roo.log('editor - hiding textarea');
45507 //            Roo.log('out')
45508 //            Roo.log(this.pushValue()); 
45509             this.editorcore.pushValue();
45510             
45511             this.el.addClass('x-hidden');
45512             this.el.dom.setAttribute('tabIndex', -1);
45513             
45514             for (var i = 0; i < this.toolbars.length; i++) {
45515                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45516                     this.toolbars[i].tb.show();
45517                     this.toolbars[i].footer.show();
45518                 }
45519             }
45520             
45521             //this.deferFocus();
45522         }
45523         
45524         this.setSize(this.wrap.getSize());
45525         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45526         
45527         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45528     },
45529  
45530     // private (for BoxComponent)
45531     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45532
45533     // private (for BoxComponent)
45534     getResizeEl : function(){
45535         return this.wrap;
45536     },
45537
45538     // private (for BoxComponent)
45539     getPositionEl : function(){
45540         return this.wrap;
45541     },
45542
45543     // private
45544     initEvents : function(){
45545         this.originalValue = this.getValue();
45546     },
45547
45548     /**
45549      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45550      * @method
45551      */
45552     markInvalid : Roo.emptyFn,
45553     /**
45554      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45555      * @method
45556      */
45557     clearInvalid : Roo.emptyFn,
45558
45559     setValue : function(v){
45560         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45561         this.editorcore.pushValue();
45562     },
45563
45564      
45565     // private
45566     deferFocus : function(){
45567         this.focus.defer(10, this);
45568     },
45569
45570     // doc'ed in Field
45571     focus : function(){
45572         this.editorcore.focus();
45573         
45574     },
45575       
45576
45577     // private
45578     onDestroy : function(){
45579         
45580         
45581         
45582         if(this.rendered){
45583             
45584             for (var i =0; i < this.toolbars.length;i++) {
45585                 // fixme - ask toolbars for heights?
45586                 this.toolbars[i].onDestroy();
45587             }
45588             
45589             this.wrap.dom.innerHTML = '';
45590             this.wrap.remove();
45591         }
45592     },
45593
45594     // private
45595     onFirstFocus : function(){
45596         //Roo.log("onFirstFocus");
45597         this.editorcore.onFirstFocus();
45598          for (var i =0; i < this.toolbars.length;i++) {
45599             this.toolbars[i].onFirstFocus();
45600         }
45601         
45602     },
45603     
45604     // private
45605     syncValue : function()
45606     {
45607         this.editorcore.syncValue();
45608     },
45609     
45610     pushValue : function()
45611     {
45612         this.editorcore.pushValue();
45613     },
45614     
45615     setStylesheets : function(stylesheets)
45616     {
45617         this.editorcore.setStylesheets(stylesheets);
45618     },
45619     
45620     removeStylesheets : function()
45621     {
45622         this.editorcore.removeStylesheets();
45623     }
45624      
45625     
45626     // hide stuff that is not compatible
45627     /**
45628      * @event blur
45629      * @hide
45630      */
45631     /**
45632      * @event change
45633      * @hide
45634      */
45635     /**
45636      * @event focus
45637      * @hide
45638      */
45639     /**
45640      * @event specialkey
45641      * @hide
45642      */
45643     /**
45644      * @cfg {String} fieldClass @hide
45645      */
45646     /**
45647      * @cfg {String} focusClass @hide
45648      */
45649     /**
45650      * @cfg {String} autoCreate @hide
45651      */
45652     /**
45653      * @cfg {String} inputType @hide
45654      */
45655     /**
45656      * @cfg {String} invalidClass @hide
45657      */
45658     /**
45659      * @cfg {String} invalidText @hide
45660      */
45661     /**
45662      * @cfg {String} msgFx @hide
45663      */
45664     /**
45665      * @cfg {String} validateOnBlur @hide
45666      */
45667 });
45668  
45669     // <script type="text/javascript">
45670 /*
45671  * Based on
45672  * Ext JS Library 1.1.1
45673  * Copyright(c) 2006-2007, Ext JS, LLC.
45674  *  
45675  
45676  */
45677
45678 /**
45679  * @class Roo.form.HtmlEditorToolbar1
45680  * Basic Toolbar
45681  * 
45682  * Usage:
45683  *
45684  new Roo.form.HtmlEditor({
45685     ....
45686     toolbars : [
45687         new Roo.form.HtmlEditorToolbar1({
45688             disable : { fonts: 1 , format: 1, ..., ... , ...],
45689             btns : [ .... ]
45690         })
45691     }
45692      
45693  * 
45694  * @cfg {Object} disable List of elements to disable..
45695  * @cfg {Array} btns List of additional buttons.
45696  * 
45697  * 
45698  * NEEDS Extra CSS? 
45699  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45700  */
45701  
45702 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45703 {
45704     
45705     Roo.apply(this, config);
45706     
45707     // default disabled, based on 'good practice'..
45708     this.disable = this.disable || {};
45709     Roo.applyIf(this.disable, {
45710         fontSize : true,
45711         colors : true,
45712         specialElements : true
45713     });
45714     
45715     
45716     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45717     // dont call parent... till later.
45718 }
45719
45720 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45721     
45722     tb: false,
45723     
45724     rendered: false,
45725     
45726     editor : false,
45727     editorcore : false,
45728     /**
45729      * @cfg {Object} disable  List of toolbar elements to disable
45730          
45731      */
45732     disable : false,
45733     
45734     
45735      /**
45736      * @cfg {String} createLinkText The default text for the create link prompt
45737      */
45738     createLinkText : 'Please enter the URL for the link:',
45739     /**
45740      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45741      */
45742     defaultLinkValue : 'http:/'+'/',
45743    
45744     
45745       /**
45746      * @cfg {Array} fontFamilies An array of available font families
45747      */
45748     fontFamilies : [
45749         'Arial',
45750         'Courier New',
45751         'Tahoma',
45752         'Times New Roman',
45753         'Verdana'
45754     ],
45755     
45756     specialChars : [
45757            "&#169;",
45758           "&#174;",     
45759           "&#8482;",    
45760           "&#163;" ,    
45761          // "&#8212;",    
45762           "&#8230;",    
45763           "&#247;" ,    
45764         //  "&#225;" ,     ?? a acute?
45765            "&#8364;"    , //Euro
45766        //   "&#8220;"    ,
45767         //  "&#8221;"    ,
45768         //  "&#8226;"    ,
45769           "&#176;"  //   , // degrees
45770
45771          // "&#233;"     , // e ecute
45772          // "&#250;"     , // u ecute?
45773     ],
45774     
45775     specialElements : [
45776         {
45777             text: "Insert Table",
45778             xtype: 'MenuItem',
45779             xns : Roo.Menu,
45780             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45781                 
45782         },
45783         {    
45784             text: "Insert Image",
45785             xtype: 'MenuItem',
45786             xns : Roo.Menu,
45787             ihtml : '<img src="about:blank"/>'
45788             
45789         }
45790         
45791          
45792     ],
45793     
45794     
45795     inputElements : [ 
45796             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45797             "input:submit", "input:button", "select", "textarea", "label" ],
45798     formats : [
45799         ["p"] ,  
45800         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45801         ["pre"],[ "code"], 
45802         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45803         ['div'],['span'],
45804         ['sup'],['sub']
45805     ],
45806     
45807     cleanStyles : [
45808         "font-size"
45809     ],
45810      /**
45811      * @cfg {String} defaultFont default font to use.
45812      */
45813     defaultFont: 'tahoma',
45814    
45815     fontSelect : false,
45816     
45817     
45818     formatCombo : false,
45819     
45820     init : function(editor)
45821     {
45822         this.editor = editor;
45823         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45824         var editorcore = this.editorcore;
45825         
45826         var _t = this;
45827         
45828         var fid = editorcore.frameId;
45829         var etb = this;
45830         function btn(id, toggle, handler){
45831             var xid = fid + '-'+ id ;
45832             return {
45833                 id : xid,
45834                 cmd : id,
45835                 cls : 'x-btn-icon x-edit-'+id,
45836                 enableToggle:toggle !== false,
45837                 scope: _t, // was editor...
45838                 handler:handler||_t.relayBtnCmd,
45839                 clickEvent:'mousedown',
45840                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45841                 tabIndex:-1
45842             };
45843         }
45844         
45845         
45846         
45847         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45848         this.tb = tb;
45849          // stop form submits
45850         tb.el.on('click', function(e){
45851             e.preventDefault(); // what does this do?
45852         });
45853
45854         if(!this.disable.font) { // && !Roo.isSafari){
45855             /* why no safari for fonts 
45856             editor.fontSelect = tb.el.createChild({
45857                 tag:'select',
45858                 tabIndex: -1,
45859                 cls:'x-font-select',
45860                 html: this.createFontOptions()
45861             });
45862             
45863             editor.fontSelect.on('change', function(){
45864                 var font = editor.fontSelect.dom.value;
45865                 editor.relayCmd('fontname', font);
45866                 editor.deferFocus();
45867             }, editor);
45868             
45869             tb.add(
45870                 editor.fontSelect.dom,
45871                 '-'
45872             );
45873             */
45874             
45875         };
45876         if(!this.disable.formats){
45877             this.formatCombo = new Roo.form.ComboBox({
45878                 store: new Roo.data.SimpleStore({
45879                     id : 'tag',
45880                     fields: ['tag'],
45881                     data : this.formats // from states.js
45882                 }),
45883                 blockFocus : true,
45884                 name : '',
45885                 //autoCreate : {tag: "div",  size: "20"},
45886                 displayField:'tag',
45887                 typeAhead: false,
45888                 mode: 'local',
45889                 editable : false,
45890                 triggerAction: 'all',
45891                 emptyText:'Add tag',
45892                 selectOnFocus:true,
45893                 width:135,
45894                 listeners : {
45895                     'select': function(c, r, i) {
45896                         editorcore.insertTag(r.get('tag'));
45897                         editor.focus();
45898                     }
45899                 }
45900
45901             });
45902             tb.addField(this.formatCombo);
45903             
45904         }
45905         
45906         if(!this.disable.format){
45907             tb.add(
45908                 btn('bold'),
45909                 btn('italic'),
45910                 btn('underline'),
45911                 btn('strikethrough')
45912             );
45913         };
45914         if(!this.disable.fontSize){
45915             tb.add(
45916                 '-',
45917                 
45918                 
45919                 btn('increasefontsize', false, editorcore.adjustFont),
45920                 btn('decreasefontsize', false, editorcore.adjustFont)
45921             );
45922         };
45923         
45924         
45925         if(!this.disable.colors){
45926             tb.add(
45927                 '-', {
45928                     id:editorcore.frameId +'-forecolor',
45929                     cls:'x-btn-icon x-edit-forecolor',
45930                     clickEvent:'mousedown',
45931                     tooltip: this.buttonTips['forecolor'] || undefined,
45932                     tabIndex:-1,
45933                     menu : new Roo.menu.ColorMenu({
45934                         allowReselect: true,
45935                         focus: Roo.emptyFn,
45936                         value:'000000',
45937                         plain:true,
45938                         selectHandler: function(cp, color){
45939                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45940                             editor.deferFocus();
45941                         },
45942                         scope: editorcore,
45943                         clickEvent:'mousedown'
45944                     })
45945                 }, {
45946                     id:editorcore.frameId +'backcolor',
45947                     cls:'x-btn-icon x-edit-backcolor',
45948                     clickEvent:'mousedown',
45949                     tooltip: this.buttonTips['backcolor'] || undefined,
45950                     tabIndex:-1,
45951                     menu : new Roo.menu.ColorMenu({
45952                         focus: Roo.emptyFn,
45953                         value:'FFFFFF',
45954                         plain:true,
45955                         allowReselect: true,
45956                         selectHandler: function(cp, color){
45957                             if(Roo.isGecko){
45958                                 editorcore.execCmd('useCSS', false);
45959                                 editorcore.execCmd('hilitecolor', color);
45960                                 editorcore.execCmd('useCSS', true);
45961                                 editor.deferFocus();
45962                             }else{
45963                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45964                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45965                                 editor.deferFocus();
45966                             }
45967                         },
45968                         scope:editorcore,
45969                         clickEvent:'mousedown'
45970                     })
45971                 }
45972             );
45973         };
45974         // now add all the items...
45975         
45976
45977         if(!this.disable.alignments){
45978             tb.add(
45979                 '-',
45980                 btn('justifyleft'),
45981                 btn('justifycenter'),
45982                 btn('justifyright')
45983             );
45984         };
45985
45986         //if(!Roo.isSafari){
45987             if(!this.disable.links){
45988                 tb.add(
45989                     '-',
45990                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45991                 );
45992             };
45993
45994             if(!this.disable.lists){
45995                 tb.add(
45996                     '-',
45997                     btn('insertorderedlist'),
45998                     btn('insertunorderedlist')
45999                 );
46000             }
46001             if(!this.disable.sourceEdit){
46002                 tb.add(
46003                     '-',
46004                     btn('sourceedit', true, function(btn){
46005                         this.toggleSourceEdit(btn.pressed);
46006                     })
46007                 );
46008             }
46009         //}
46010         
46011         var smenu = { };
46012         // special menu.. - needs to be tidied up..
46013         if (!this.disable.special) {
46014             smenu = {
46015                 text: "&#169;",
46016                 cls: 'x-edit-none',
46017                 
46018                 menu : {
46019                     items : []
46020                 }
46021             };
46022             for (var i =0; i < this.specialChars.length; i++) {
46023                 smenu.menu.items.push({
46024                     
46025                     html: this.specialChars[i],
46026                     handler: function(a,b) {
46027                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46028                         //editor.insertAtCursor(a.html);
46029                         
46030                     },
46031                     tabIndex:-1
46032                 });
46033             }
46034             
46035             
46036             tb.add(smenu);
46037             
46038             
46039         }
46040         
46041         var cmenu = { };
46042         if (!this.disable.cleanStyles) {
46043             cmenu = {
46044                 cls: 'x-btn-icon x-btn-clear',
46045                 
46046                 menu : {
46047                     items : []
46048                 }
46049             };
46050             for (var i =0; i < this.cleanStyles.length; i++) {
46051                 cmenu.menu.items.push({
46052                     actiontype : this.cleanStyles[i],
46053                     html: 'Remove ' + this.cleanStyles[i],
46054                     handler: function(a,b) {
46055 //                        Roo.log(a);
46056 //                        Roo.log(b);
46057                         var c = Roo.get(editorcore.doc.body);
46058                         c.select('[style]').each(function(s) {
46059                             s.dom.style.removeProperty(a.actiontype);
46060                         });
46061                         editorcore.syncValue();
46062                     },
46063                     tabIndex:-1
46064                 });
46065             }
46066              cmenu.menu.items.push({
46067                 actiontype : 'tablewidths',
46068                 html: 'Remove Table Widths',
46069                 handler: function(a,b) {
46070                     editorcore.cleanTableWidths();
46071                     editorcore.syncValue();
46072                 },
46073                 tabIndex:-1
46074             });
46075             cmenu.menu.items.push({
46076                 actiontype : 'word',
46077                 html: 'Remove MS Word Formating',
46078                 handler: function(a,b) {
46079                     editorcore.cleanWord();
46080                     editorcore.syncValue();
46081                 },
46082                 tabIndex:-1
46083             });
46084             
46085             cmenu.menu.items.push({
46086                 actiontype : 'all',
46087                 html: 'Remove All Styles',
46088                 handler: function(a,b) {
46089                     
46090                     var c = Roo.get(editorcore.doc.body);
46091                     c.select('[style]').each(function(s) {
46092                         s.dom.removeAttribute('style');
46093                     });
46094                     editorcore.syncValue();
46095                 },
46096                 tabIndex:-1
46097             });
46098             
46099             cmenu.menu.items.push({
46100                 actiontype : 'all',
46101                 html: 'Remove All CSS Classes',
46102                 handler: function(a,b) {
46103                     
46104                     var c = Roo.get(editorcore.doc.body);
46105                     c.select('[class]').each(function(s) {
46106                         s.dom.removeAttribute('class');
46107                     });
46108                     editorcore.cleanWord();
46109                     editorcore.syncValue();
46110                 },
46111                 tabIndex:-1
46112             });
46113             
46114              cmenu.menu.items.push({
46115                 actiontype : 'tidy',
46116                 html: 'Tidy HTML Source',
46117                 handler: function(a,b) {
46118                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46119                     editorcore.syncValue();
46120                 },
46121                 tabIndex:-1
46122             });
46123             
46124             
46125             tb.add(cmenu);
46126         }
46127          
46128         if (!this.disable.specialElements) {
46129             var semenu = {
46130                 text: "Other;",
46131                 cls: 'x-edit-none',
46132                 menu : {
46133                     items : []
46134                 }
46135             };
46136             for (var i =0; i < this.specialElements.length; i++) {
46137                 semenu.menu.items.push(
46138                     Roo.apply({ 
46139                         handler: function(a,b) {
46140                             editor.insertAtCursor(this.ihtml);
46141                         }
46142                     }, this.specialElements[i])
46143                 );
46144                     
46145             }
46146             
46147             tb.add(semenu);
46148             
46149             
46150         }
46151          
46152         
46153         if (this.btns) {
46154             for(var i =0; i< this.btns.length;i++) {
46155                 var b = Roo.factory(this.btns[i],Roo.form);
46156                 b.cls =  'x-edit-none';
46157                 
46158                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46159                     b.cls += ' x-init-enable';
46160                 }
46161                 
46162                 b.scope = editorcore;
46163                 tb.add(b);
46164             }
46165         
46166         }
46167         
46168         
46169         
46170         // disable everything...
46171         
46172         this.tb.items.each(function(item){
46173             
46174            if(
46175                 item.id != editorcore.frameId+ '-sourceedit' && 
46176                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46177             ){
46178                 
46179                 item.disable();
46180             }
46181         });
46182         this.rendered = true;
46183         
46184         // the all the btns;
46185         editor.on('editorevent', this.updateToolbar, this);
46186         // other toolbars need to implement this..
46187         //editor.on('editmodechange', this.updateToolbar, this);
46188     },
46189     
46190     
46191     relayBtnCmd : function(btn) {
46192         this.editorcore.relayCmd(btn.cmd);
46193     },
46194     // private used internally
46195     createLink : function(){
46196         Roo.log("create link?");
46197         var url = prompt(this.createLinkText, this.defaultLinkValue);
46198         if(url && url != 'http:/'+'/'){
46199             this.editorcore.relayCmd('createlink', url);
46200         }
46201     },
46202
46203     
46204     /**
46205      * Protected method that will not generally be called directly. It triggers
46206      * a toolbar update by reading the markup state of the current selection in the editor.
46207      */
46208     updateToolbar: function(){
46209
46210         if(!this.editorcore.activated){
46211             this.editor.onFirstFocus();
46212             return;
46213         }
46214
46215         var btns = this.tb.items.map, 
46216             doc = this.editorcore.doc,
46217             frameId = this.editorcore.frameId;
46218
46219         if(!this.disable.font && !Roo.isSafari){
46220             /*
46221             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46222             if(name != this.fontSelect.dom.value){
46223                 this.fontSelect.dom.value = name;
46224             }
46225             */
46226         }
46227         if(!this.disable.format){
46228             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46229             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46230             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46231             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46232         }
46233         if(!this.disable.alignments){
46234             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46235             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46236             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46237         }
46238         if(!Roo.isSafari && !this.disable.lists){
46239             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46240             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46241         }
46242         
46243         var ans = this.editorcore.getAllAncestors();
46244         if (this.formatCombo) {
46245             
46246             
46247             var store = this.formatCombo.store;
46248             this.formatCombo.setValue("");
46249             for (var i =0; i < ans.length;i++) {
46250                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46251                     // select it..
46252                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46253                     break;
46254                 }
46255             }
46256         }
46257         
46258         
46259         
46260         // hides menus... - so this cant be on a menu...
46261         Roo.menu.MenuMgr.hideAll();
46262
46263         //this.editorsyncValue();
46264     },
46265    
46266     
46267     createFontOptions : function(){
46268         var buf = [], fs = this.fontFamilies, ff, lc;
46269         
46270         
46271         
46272         for(var i = 0, len = fs.length; i< len; i++){
46273             ff = fs[i];
46274             lc = ff.toLowerCase();
46275             buf.push(
46276                 '<option value="',lc,'" style="font-family:',ff,';"',
46277                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46278                     ff,
46279                 '</option>'
46280             );
46281         }
46282         return buf.join('');
46283     },
46284     
46285     toggleSourceEdit : function(sourceEditMode){
46286         
46287         Roo.log("toolbar toogle");
46288         if(sourceEditMode === undefined){
46289             sourceEditMode = !this.sourceEditMode;
46290         }
46291         this.sourceEditMode = sourceEditMode === true;
46292         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46293         // just toggle the button?
46294         if(btn.pressed !== this.sourceEditMode){
46295             btn.toggle(this.sourceEditMode);
46296             return;
46297         }
46298         
46299         if(sourceEditMode){
46300             Roo.log("disabling buttons");
46301             this.tb.items.each(function(item){
46302                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46303                     item.disable();
46304                 }
46305             });
46306           
46307         }else{
46308             Roo.log("enabling buttons");
46309             if(this.editorcore.initialized){
46310                 this.tb.items.each(function(item){
46311                     item.enable();
46312                 });
46313             }
46314             
46315         }
46316         Roo.log("calling toggole on editor");
46317         // tell the editor that it's been pressed..
46318         this.editor.toggleSourceEdit(sourceEditMode);
46319        
46320     },
46321      /**
46322      * Object collection of toolbar tooltips for the buttons in the editor. The key
46323      * is the command id associated with that button and the value is a valid QuickTips object.
46324      * For example:
46325 <pre><code>
46326 {
46327     bold : {
46328         title: 'Bold (Ctrl+B)',
46329         text: 'Make the selected text bold.',
46330         cls: 'x-html-editor-tip'
46331     },
46332     italic : {
46333         title: 'Italic (Ctrl+I)',
46334         text: 'Make the selected text italic.',
46335         cls: 'x-html-editor-tip'
46336     },
46337     ...
46338 </code></pre>
46339     * @type Object
46340      */
46341     buttonTips : {
46342         bold : {
46343             title: 'Bold (Ctrl+B)',
46344             text: 'Make the selected text bold.',
46345             cls: 'x-html-editor-tip'
46346         },
46347         italic : {
46348             title: 'Italic (Ctrl+I)',
46349             text: 'Make the selected text italic.',
46350             cls: 'x-html-editor-tip'
46351         },
46352         underline : {
46353             title: 'Underline (Ctrl+U)',
46354             text: 'Underline the selected text.',
46355             cls: 'x-html-editor-tip'
46356         },
46357         strikethrough : {
46358             title: 'Strikethrough',
46359             text: 'Strikethrough the selected text.',
46360             cls: 'x-html-editor-tip'
46361         },
46362         increasefontsize : {
46363             title: 'Grow Text',
46364             text: 'Increase the font size.',
46365             cls: 'x-html-editor-tip'
46366         },
46367         decreasefontsize : {
46368             title: 'Shrink Text',
46369             text: 'Decrease the font size.',
46370             cls: 'x-html-editor-tip'
46371         },
46372         backcolor : {
46373             title: 'Text Highlight Color',
46374             text: 'Change the background color of the selected text.',
46375             cls: 'x-html-editor-tip'
46376         },
46377         forecolor : {
46378             title: 'Font Color',
46379             text: 'Change the color of the selected text.',
46380             cls: 'x-html-editor-tip'
46381         },
46382         justifyleft : {
46383             title: 'Align Text Left',
46384             text: 'Align text to the left.',
46385             cls: 'x-html-editor-tip'
46386         },
46387         justifycenter : {
46388             title: 'Center Text',
46389             text: 'Center text in the editor.',
46390             cls: 'x-html-editor-tip'
46391         },
46392         justifyright : {
46393             title: 'Align Text Right',
46394             text: 'Align text to the right.',
46395             cls: 'x-html-editor-tip'
46396         },
46397         insertunorderedlist : {
46398             title: 'Bullet List',
46399             text: 'Start a bulleted list.',
46400             cls: 'x-html-editor-tip'
46401         },
46402         insertorderedlist : {
46403             title: 'Numbered List',
46404             text: 'Start a numbered list.',
46405             cls: 'x-html-editor-tip'
46406         },
46407         createlink : {
46408             title: 'Hyperlink',
46409             text: 'Make the selected text a hyperlink.',
46410             cls: 'x-html-editor-tip'
46411         },
46412         sourceedit : {
46413             title: 'Source Edit',
46414             text: 'Switch to source editing mode.',
46415             cls: 'x-html-editor-tip'
46416         }
46417     },
46418     // private
46419     onDestroy : function(){
46420         if(this.rendered){
46421             
46422             this.tb.items.each(function(item){
46423                 if(item.menu){
46424                     item.menu.removeAll();
46425                     if(item.menu.el){
46426                         item.menu.el.destroy();
46427                     }
46428                 }
46429                 item.destroy();
46430             });
46431              
46432         }
46433     },
46434     onFirstFocus: function() {
46435         this.tb.items.each(function(item){
46436            item.enable();
46437         });
46438     }
46439 });
46440
46441
46442
46443
46444 // <script type="text/javascript">
46445 /*
46446  * Based on
46447  * Ext JS Library 1.1.1
46448  * Copyright(c) 2006-2007, Ext JS, LLC.
46449  *  
46450  
46451  */
46452
46453  
46454 /**
46455  * @class Roo.form.HtmlEditor.ToolbarContext
46456  * Context Toolbar
46457  * 
46458  * Usage:
46459  *
46460  new Roo.form.HtmlEditor({
46461     ....
46462     toolbars : [
46463         { xtype: 'ToolbarStandard', styles : {} }
46464         { xtype: 'ToolbarContext', disable : {} }
46465     ]
46466 })
46467
46468      
46469  * 
46470  * @config : {Object} disable List of elements to disable.. (not done yet.)
46471  * @config : {Object} styles  Map of styles available.
46472  * 
46473  */
46474
46475 Roo.form.HtmlEditor.ToolbarContext = function(config)
46476 {
46477     
46478     Roo.apply(this, config);
46479     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46480     // dont call parent... till later.
46481     this.styles = this.styles || {};
46482 }
46483
46484  
46485
46486 Roo.form.HtmlEditor.ToolbarContext.types = {
46487     'IMG' : {
46488         width : {
46489             title: "Width",
46490             width: 40
46491         },
46492         height:  {
46493             title: "Height",
46494             width: 40
46495         },
46496         align: {
46497             title: "Align",
46498             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46499             width : 80
46500             
46501         },
46502         border: {
46503             title: "Border",
46504             width: 40
46505         },
46506         alt: {
46507             title: "Alt",
46508             width: 120
46509         },
46510         src : {
46511             title: "Src",
46512             width: 220
46513         }
46514         
46515     },
46516     'A' : {
46517         name : {
46518             title: "Name",
46519             width: 50
46520         },
46521         target:  {
46522             title: "Target",
46523             width: 120
46524         },
46525         href:  {
46526             title: "Href",
46527             width: 220
46528         } // border?
46529         
46530     },
46531     'TABLE' : {
46532         rows : {
46533             title: "Rows",
46534             width: 20
46535         },
46536         cols : {
46537             title: "Cols",
46538             width: 20
46539         },
46540         width : {
46541             title: "Width",
46542             width: 40
46543         },
46544         height : {
46545             title: "Height",
46546             width: 40
46547         },
46548         border : {
46549             title: "Border",
46550             width: 20
46551         }
46552     },
46553     'TD' : {
46554         width : {
46555             title: "Width",
46556             width: 40
46557         },
46558         height : {
46559             title: "Height",
46560             width: 40
46561         },   
46562         align: {
46563             title: "Align",
46564             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46565             width: 80
46566         },
46567         valign: {
46568             title: "Valign",
46569             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46570             width: 80
46571         },
46572         colspan: {
46573             title: "Colspan",
46574             width: 20
46575             
46576         },
46577          'font-family'  : {
46578             title : "Font",
46579             style : 'fontFamily',
46580             displayField: 'display',
46581             optname : 'font-family',
46582             width: 140
46583         }
46584     },
46585     'INPUT' : {
46586         name : {
46587             title: "name",
46588             width: 120
46589         },
46590         value : {
46591             title: "Value",
46592             width: 120
46593         },
46594         width : {
46595             title: "Width",
46596             width: 40
46597         }
46598     },
46599     'LABEL' : {
46600         'for' : {
46601             title: "For",
46602             width: 120
46603         }
46604     },
46605     'TEXTAREA' : {
46606           name : {
46607             title: "name",
46608             width: 120
46609         },
46610         rows : {
46611             title: "Rows",
46612             width: 20
46613         },
46614         cols : {
46615             title: "Cols",
46616             width: 20
46617         }
46618     },
46619     'SELECT' : {
46620         name : {
46621             title: "name",
46622             width: 120
46623         },
46624         selectoptions : {
46625             title: "Options",
46626             width: 200
46627         }
46628     },
46629     
46630     // should we really allow this??
46631     // should this just be 
46632     'BODY' : {
46633         title : {
46634             title: "Title",
46635             width: 200,
46636             disabled : true
46637         }
46638     },
46639     'SPAN' : {
46640         'font-family'  : {
46641             title : "Font",
46642             style : 'fontFamily',
46643             displayField: 'display',
46644             optname : 'font-family',
46645             width: 140
46646         }
46647     },
46648     'DIV' : {
46649         'font-family'  : {
46650             title : "Font",
46651             style : 'fontFamily',
46652             displayField: 'display',
46653             optname : 'font-family',
46654             width: 140
46655         }
46656     },
46657      'P' : {
46658         'font-family'  : {
46659             title : "Font",
46660             style : 'fontFamily',
46661             displayField: 'display',
46662             optname : 'font-family',
46663             width: 140
46664         }
46665     },
46666     
46667     '*' : {
46668         // empty..
46669     }
46670
46671 };
46672
46673 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46674 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46675
46676 Roo.form.HtmlEditor.ToolbarContext.options = {
46677         'font-family'  : [ 
46678                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46679                 [ 'Courier New', 'Courier New'],
46680                 [ 'Tahoma', 'Tahoma'],
46681                 [ 'Times New Roman,serif', 'Times'],
46682                 [ 'Verdana','Verdana' ]
46683         ]
46684 };
46685
46686 // fixme - these need to be configurable..
46687  
46688
46689 //Roo.form.HtmlEditor.ToolbarContext.types
46690
46691
46692 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46693     
46694     tb: false,
46695     
46696     rendered: false,
46697     
46698     editor : false,
46699     editorcore : false,
46700     /**
46701      * @cfg {Object} disable  List of toolbar elements to disable
46702          
46703      */
46704     disable : false,
46705     /**
46706      * @cfg {Object} styles List of styles 
46707      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46708      *
46709      * These must be defined in the page, so they get rendered correctly..
46710      * .headline { }
46711      * TD.underline { }
46712      * 
46713      */
46714     styles : false,
46715     
46716     options: false,
46717     
46718     toolbars : false,
46719     
46720     init : function(editor)
46721     {
46722         this.editor = editor;
46723         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46724         var editorcore = this.editorcore;
46725         
46726         var fid = editorcore.frameId;
46727         var etb = this;
46728         function btn(id, toggle, handler){
46729             var xid = fid + '-'+ id ;
46730             return {
46731                 id : xid,
46732                 cmd : id,
46733                 cls : 'x-btn-icon x-edit-'+id,
46734                 enableToggle:toggle !== false,
46735                 scope: editorcore, // was editor...
46736                 handler:handler||editorcore.relayBtnCmd,
46737                 clickEvent:'mousedown',
46738                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46739                 tabIndex:-1
46740             };
46741         }
46742         // create a new element.
46743         var wdiv = editor.wrap.createChild({
46744                 tag: 'div'
46745             }, editor.wrap.dom.firstChild.nextSibling, true);
46746         
46747         // can we do this more than once??
46748         
46749          // stop form submits
46750       
46751  
46752         // disable everything...
46753         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46754         this.toolbars = {};
46755            
46756         for (var i in  ty) {
46757           
46758             this.toolbars[i] = this.buildToolbar(ty[i],i);
46759         }
46760         this.tb = this.toolbars.BODY;
46761         this.tb.el.show();
46762         this.buildFooter();
46763         this.footer.show();
46764         editor.on('hide', function( ) { this.footer.hide() }, this);
46765         editor.on('show', function( ) { this.footer.show() }, this);
46766         
46767          
46768         this.rendered = true;
46769         
46770         // the all the btns;
46771         editor.on('editorevent', this.updateToolbar, this);
46772         // other toolbars need to implement this..
46773         //editor.on('editmodechange', this.updateToolbar, this);
46774     },
46775     
46776     
46777     
46778     /**
46779      * Protected method that will not generally be called directly. It triggers
46780      * a toolbar update by reading the markup state of the current selection in the editor.
46781      *
46782      * Note you can force an update by calling on('editorevent', scope, false)
46783      */
46784     updateToolbar: function(editor,ev,sel){
46785
46786         //Roo.log(ev);
46787         // capture mouse up - this is handy for selecting images..
46788         // perhaps should go somewhere else...
46789         if(!this.editorcore.activated){
46790              this.editor.onFirstFocus();
46791             return;
46792         }
46793         
46794         
46795         
46796         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46797         // selectNode - might want to handle IE?
46798         if (ev &&
46799             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46800             ev.target && ev.target.tagName == 'IMG') {
46801             // they have click on an image...
46802             // let's see if we can change the selection...
46803             sel = ev.target;
46804          
46805               var nodeRange = sel.ownerDocument.createRange();
46806             try {
46807                 nodeRange.selectNode(sel);
46808             } catch (e) {
46809                 nodeRange.selectNodeContents(sel);
46810             }
46811             //nodeRange.collapse(true);
46812             var s = this.editorcore.win.getSelection();
46813             s.removeAllRanges();
46814             s.addRange(nodeRange);
46815         }  
46816         
46817       
46818         var updateFooter = sel ? false : true;
46819         
46820         
46821         var ans = this.editorcore.getAllAncestors();
46822         
46823         // pick
46824         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46825         
46826         if (!sel) { 
46827             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46828             sel = sel ? sel : this.editorcore.doc.body;
46829             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46830             
46831         }
46832         // pick a menu that exists..
46833         var tn = sel.tagName.toUpperCase();
46834         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46835         
46836         tn = sel.tagName.toUpperCase();
46837         
46838         var lastSel = this.tb.selectedNode;
46839         
46840         this.tb.selectedNode = sel;
46841         
46842         // if current menu does not match..
46843         
46844         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46845                 
46846             this.tb.el.hide();
46847             ///console.log("show: " + tn);
46848             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46849             this.tb.el.show();
46850             // update name
46851             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46852             
46853             
46854             // update attributes
46855             if (this.tb.fields) {
46856                 this.tb.fields.each(function(e) {
46857                     if (e.stylename) {
46858                         e.setValue(sel.style[e.stylename]);
46859                         return;
46860                     } 
46861                    e.setValue(sel.getAttribute(e.attrname));
46862                 });
46863             }
46864             
46865             var hasStyles = false;
46866             for(var i in this.styles) {
46867                 hasStyles = true;
46868                 break;
46869             }
46870             
46871             // update styles
46872             if (hasStyles) { 
46873                 var st = this.tb.fields.item(0);
46874                 
46875                 st.store.removeAll();
46876                
46877                 
46878                 var cn = sel.className.split(/\s+/);
46879                 
46880                 var avs = [];
46881                 if (this.styles['*']) {
46882                     
46883                     Roo.each(this.styles['*'], function(v) {
46884                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46885                     });
46886                 }
46887                 if (this.styles[tn]) { 
46888                     Roo.each(this.styles[tn], function(v) {
46889                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46890                     });
46891                 }
46892                 
46893                 st.store.loadData(avs);
46894                 st.collapse();
46895                 st.setValue(cn);
46896             }
46897             // flag our selected Node.
46898             this.tb.selectedNode = sel;
46899            
46900            
46901             Roo.menu.MenuMgr.hideAll();
46902
46903         }
46904         
46905         if (!updateFooter) {
46906             //this.footDisp.dom.innerHTML = ''; 
46907             return;
46908         }
46909         // update the footer
46910         //
46911         var html = '';
46912         
46913         this.footerEls = ans.reverse();
46914         Roo.each(this.footerEls, function(a,i) {
46915             if (!a) { return; }
46916             html += html.length ? ' &gt; '  :  '';
46917             
46918             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46919             
46920         });
46921        
46922         // 
46923         var sz = this.footDisp.up('td').getSize();
46924         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46925         this.footDisp.dom.style.marginLeft = '5px';
46926         
46927         this.footDisp.dom.style.overflow = 'hidden';
46928         
46929         this.footDisp.dom.innerHTML = html;
46930             
46931         //this.editorsyncValue();
46932     },
46933      
46934     
46935    
46936        
46937     // private
46938     onDestroy : function(){
46939         if(this.rendered){
46940             
46941             this.tb.items.each(function(item){
46942                 if(item.menu){
46943                     item.menu.removeAll();
46944                     if(item.menu.el){
46945                         item.menu.el.destroy();
46946                     }
46947                 }
46948                 item.destroy();
46949             });
46950              
46951         }
46952     },
46953     onFirstFocus: function() {
46954         // need to do this for all the toolbars..
46955         this.tb.items.each(function(item){
46956            item.enable();
46957         });
46958     },
46959     buildToolbar: function(tlist, nm)
46960     {
46961         var editor = this.editor;
46962         var editorcore = this.editorcore;
46963          // create a new element.
46964         var wdiv = editor.wrap.createChild({
46965                 tag: 'div'
46966             }, editor.wrap.dom.firstChild.nextSibling, true);
46967         
46968        
46969         var tb = new Roo.Toolbar(wdiv);
46970         // add the name..
46971         
46972         tb.add(nm+ ":&nbsp;");
46973         
46974         var styles = [];
46975         for(var i in this.styles) {
46976             styles.push(i);
46977         }
46978         
46979         // styles...
46980         if (styles && styles.length) {
46981             
46982             // this needs a multi-select checkbox...
46983             tb.addField( new Roo.form.ComboBox({
46984                 store: new Roo.data.SimpleStore({
46985                     id : 'val',
46986                     fields: ['val', 'selected'],
46987                     data : [] 
46988                 }),
46989                 name : '-roo-edit-className',
46990                 attrname : 'className',
46991                 displayField: 'val',
46992                 typeAhead: false,
46993                 mode: 'local',
46994                 editable : false,
46995                 triggerAction: 'all',
46996                 emptyText:'Select Style',
46997                 selectOnFocus:true,
46998                 width: 130,
46999                 listeners : {
47000                     'select': function(c, r, i) {
47001                         // initial support only for on class per el..
47002                         tb.selectedNode.className =  r ? r.get('val') : '';
47003                         editorcore.syncValue();
47004                     }
47005                 }
47006     
47007             }));
47008         }
47009         
47010         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47011         var tbops = tbc.options;
47012         
47013         for (var i in tlist) {
47014             
47015             var item = tlist[i];
47016             tb.add(item.title + ":&nbsp;");
47017             
47018             
47019             //optname == used so you can configure the options available..
47020             var opts = item.opts ? item.opts : false;
47021             if (item.optname) {
47022                 opts = tbops[item.optname];
47023            
47024             }
47025             
47026             if (opts) {
47027                 // opts == pulldown..
47028                 tb.addField( new Roo.form.ComboBox({
47029                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47030                         id : 'val',
47031                         fields: ['val', 'display'],
47032                         data : opts  
47033                     }),
47034                     name : '-roo-edit-' + i,
47035                     attrname : i,
47036                     stylename : item.style ? item.style : false,
47037                     displayField: item.displayField ? item.displayField : 'val',
47038                     valueField :  'val',
47039                     typeAhead: false,
47040                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47041                     editable : false,
47042                     triggerAction: 'all',
47043                     emptyText:'Select',
47044                     selectOnFocus:true,
47045                     width: item.width ? item.width  : 130,
47046                     listeners : {
47047                         'select': function(c, r, i) {
47048                             if (c.stylename) {
47049                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47050                                 return;
47051                             }
47052                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47053                         }
47054                     }
47055
47056                 }));
47057                 continue;
47058                     
47059                  
47060                 
47061                 tb.addField( new Roo.form.TextField({
47062                     name: i,
47063                     width: 100,
47064                     //allowBlank:false,
47065                     value: ''
47066                 }));
47067                 continue;
47068             }
47069             tb.addField( new Roo.form.TextField({
47070                 name: '-roo-edit-' + i,
47071                 attrname : i,
47072                 
47073                 width: item.width,
47074                 //allowBlank:true,
47075                 value: '',
47076                 listeners: {
47077                     'change' : function(f, nv, ov) {
47078                         tb.selectedNode.setAttribute(f.attrname, nv);
47079                         editorcore.syncValue();
47080                     }
47081                 }
47082             }));
47083              
47084         }
47085         
47086         var _this = this;
47087         
47088         if(nm == 'BODY'){
47089             tb.addSeparator();
47090         
47091             tb.addButton( {
47092                 text: 'Stylesheets',
47093
47094                 listeners : {
47095                     click : function ()
47096                     {
47097                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47098                     }
47099                 }
47100             });
47101         }
47102         
47103         tb.addFill();
47104         tb.addButton( {
47105             text: 'Remove Tag',
47106     
47107             listeners : {
47108                 click : function ()
47109                 {
47110                     // remove
47111                     // undo does not work.
47112                      
47113                     var sn = tb.selectedNode;
47114                     
47115                     var pn = sn.parentNode;
47116                     
47117                     var stn =  sn.childNodes[0];
47118                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47119                     while (sn.childNodes.length) {
47120                         var node = sn.childNodes[0];
47121                         sn.removeChild(node);
47122                         //Roo.log(node);
47123                         pn.insertBefore(node, sn);
47124                         
47125                     }
47126                     pn.removeChild(sn);
47127                     var range = editorcore.createRange();
47128         
47129                     range.setStart(stn,0);
47130                     range.setEnd(en,0); //????
47131                     //range.selectNode(sel);
47132                     
47133                     
47134                     var selection = editorcore.getSelection();
47135                     selection.removeAllRanges();
47136                     selection.addRange(range);
47137                     
47138                     
47139                     
47140                     //_this.updateToolbar(null, null, pn);
47141                     _this.updateToolbar(null, null, null);
47142                     _this.footDisp.dom.innerHTML = ''; 
47143                 }
47144             }
47145             
47146                     
47147                 
47148             
47149         });
47150         
47151         
47152         tb.el.on('click', function(e){
47153             e.preventDefault(); // what does this do?
47154         });
47155         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47156         tb.el.hide();
47157         tb.name = nm;
47158         // dont need to disable them... as they will get hidden
47159         return tb;
47160          
47161         
47162     },
47163     buildFooter : function()
47164     {
47165         
47166         var fel = this.editor.wrap.createChild();
47167         this.footer = new Roo.Toolbar(fel);
47168         // toolbar has scrolly on left / right?
47169         var footDisp= new Roo.Toolbar.Fill();
47170         var _t = this;
47171         this.footer.add(
47172             {
47173                 text : '&lt;',
47174                 xtype: 'Button',
47175                 handler : function() {
47176                     _t.footDisp.scrollTo('left',0,true)
47177                 }
47178             }
47179         );
47180         this.footer.add( footDisp );
47181         this.footer.add( 
47182             {
47183                 text : '&gt;',
47184                 xtype: 'Button',
47185                 handler : function() {
47186                     // no animation..
47187                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47188                 }
47189             }
47190         );
47191         var fel = Roo.get(footDisp.el);
47192         fel.addClass('x-editor-context');
47193         this.footDispWrap = fel; 
47194         this.footDispWrap.overflow  = 'hidden';
47195         
47196         this.footDisp = fel.createChild();
47197         this.footDispWrap.on('click', this.onContextClick, this)
47198         
47199         
47200     },
47201     onContextClick : function (ev,dom)
47202     {
47203         ev.preventDefault();
47204         var  cn = dom.className;
47205         //Roo.log(cn);
47206         if (!cn.match(/x-ed-loc-/)) {
47207             return;
47208         }
47209         var n = cn.split('-').pop();
47210         var ans = this.footerEls;
47211         var sel = ans[n];
47212         
47213          // pick
47214         var range = this.editorcore.createRange();
47215         
47216         range.selectNodeContents(sel);
47217         //range.selectNode(sel);
47218         
47219         
47220         var selection = this.editorcore.getSelection();
47221         selection.removeAllRanges();
47222         selection.addRange(range);
47223         
47224         
47225         
47226         this.updateToolbar(null, null, sel);
47227         
47228         
47229     }
47230     
47231     
47232     
47233     
47234     
47235 });
47236
47237
47238
47239
47240
47241 /*
47242  * Based on:
47243  * Ext JS Library 1.1.1
47244  * Copyright(c) 2006-2007, Ext JS, LLC.
47245  *
47246  * Originally Released Under LGPL - original licence link has changed is not relivant.
47247  *
47248  * Fork - LGPL
47249  * <script type="text/javascript">
47250  */
47251  
47252 /**
47253  * @class Roo.form.BasicForm
47254  * @extends Roo.util.Observable
47255  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47256  * @constructor
47257  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47258  * @param {Object} config Configuration options
47259  */
47260 Roo.form.BasicForm = function(el, config){
47261     this.allItems = [];
47262     this.childForms = [];
47263     Roo.apply(this, config);
47264     /*
47265      * The Roo.form.Field items in this form.
47266      * @type MixedCollection
47267      */
47268      
47269      
47270     this.items = new Roo.util.MixedCollection(false, function(o){
47271         return o.id || (o.id = Roo.id());
47272     });
47273     this.addEvents({
47274         /**
47275          * @event beforeaction
47276          * Fires before any action is performed. Return false to cancel the action.
47277          * @param {Form} this
47278          * @param {Action} action The action to be performed
47279          */
47280         beforeaction: true,
47281         /**
47282          * @event actionfailed
47283          * Fires when an action fails.
47284          * @param {Form} this
47285          * @param {Action} action The action that failed
47286          */
47287         actionfailed : true,
47288         /**
47289          * @event actioncomplete
47290          * Fires when an action is completed.
47291          * @param {Form} this
47292          * @param {Action} action The action that completed
47293          */
47294         actioncomplete : true
47295     });
47296     if(el){
47297         this.initEl(el);
47298     }
47299     Roo.form.BasicForm.superclass.constructor.call(this);
47300     
47301     Roo.form.BasicForm.popover.apply();
47302 };
47303
47304 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47305     /**
47306      * @cfg {String} method
47307      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47308      */
47309     /**
47310      * @cfg {DataReader} reader
47311      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47312      * This is optional as there is built-in support for processing JSON.
47313      */
47314     /**
47315      * @cfg {DataReader} errorReader
47316      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47317      * This is completely optional as there is built-in support for processing JSON.
47318      */
47319     /**
47320      * @cfg {String} url
47321      * The URL to use for form actions if one isn't supplied in the action options.
47322      */
47323     /**
47324      * @cfg {Boolean} fileUpload
47325      * Set to true if this form is a file upload.
47326      */
47327      
47328     /**
47329      * @cfg {Object} baseParams
47330      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47331      */
47332      /**
47333      
47334     /**
47335      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47336      */
47337     timeout: 30,
47338
47339     // private
47340     activeAction : null,
47341
47342     /**
47343      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47344      * or setValues() data instead of when the form was first created.
47345      */
47346     trackResetOnLoad : false,
47347     
47348     
47349     /**
47350      * childForms - used for multi-tab forms
47351      * @type {Array}
47352      */
47353     childForms : false,
47354     
47355     /**
47356      * allItems - full list of fields.
47357      * @type {Array}
47358      */
47359     allItems : false,
47360     
47361     /**
47362      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47363      * element by passing it or its id or mask the form itself by passing in true.
47364      * @type Mixed
47365      */
47366     waitMsgTarget : false,
47367     
47368     /**
47369      * @type Boolean
47370      */
47371     disableMask : false,
47372     
47373     /**
47374      * @cfg {Boolean} errorMask (true|false) default false
47375      */
47376     errorMask : false,
47377     
47378     /**
47379      * @cfg {Number} maskOffset Default 100
47380      */
47381     maskOffset : 100,
47382
47383     // private
47384     initEl : function(el){
47385         this.el = Roo.get(el);
47386         this.id = this.el.id || Roo.id();
47387         this.el.on('submit', this.onSubmit, this);
47388         this.el.addClass('x-form');
47389     },
47390
47391     // private
47392     onSubmit : function(e){
47393         e.stopEvent();
47394     },
47395
47396     /**
47397      * Returns true if client-side validation on the form is successful.
47398      * @return Boolean
47399      */
47400     isValid : function(){
47401         var valid = true;
47402         var target = false;
47403         this.items.each(function(f){
47404             if(f.validate()){
47405                 return;
47406             }
47407             
47408             valid = false;
47409                 
47410             if(!target && f.el.isVisible(true)){
47411                 target = f;
47412             }
47413         });
47414         
47415         if(this.errorMask && !valid){
47416             Roo.form.BasicForm.popover.mask(this, target);
47417         }
47418         
47419         return valid;
47420     },
47421
47422     /**
47423      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47424      * @return Boolean
47425      */
47426     isDirty : function(){
47427         var dirty = false;
47428         this.items.each(function(f){
47429            if(f.isDirty()){
47430                dirty = true;
47431                return false;
47432            }
47433         });
47434         return dirty;
47435     },
47436     
47437     /**
47438      * Returns true if any fields in this form have changed since their original load. (New version)
47439      * @return Boolean
47440      */
47441     
47442     hasChanged : function()
47443     {
47444         var dirty = false;
47445         this.items.each(function(f){
47446            if(f.hasChanged()){
47447                dirty = true;
47448                return false;
47449            }
47450         });
47451         return dirty;
47452         
47453     },
47454     /**
47455      * Resets all hasChanged to 'false' -
47456      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47457      * So hasChanged storage is only to be used for this purpose
47458      * @return Boolean
47459      */
47460     resetHasChanged : function()
47461     {
47462         this.items.each(function(f){
47463            f.resetHasChanged();
47464         });
47465         
47466     },
47467     
47468     
47469     /**
47470      * Performs a predefined action (submit or load) or custom actions you define on this form.
47471      * @param {String} actionName The name of the action type
47472      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47473      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47474      * accept other config options):
47475      * <pre>
47476 Property          Type             Description
47477 ----------------  ---------------  ----------------------------------------------------------------------------------
47478 url               String           The url for the action (defaults to the form's url)
47479 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47480 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47481 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47482                                    validate the form on the client (defaults to false)
47483      * </pre>
47484      * @return {BasicForm} this
47485      */
47486     doAction : function(action, options){
47487         if(typeof action == 'string'){
47488             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47489         }
47490         if(this.fireEvent('beforeaction', this, action) !== false){
47491             this.beforeAction(action);
47492             action.run.defer(100, action);
47493         }
47494         return this;
47495     },
47496
47497     /**
47498      * Shortcut to do a submit action.
47499      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47500      * @return {BasicForm} this
47501      */
47502     submit : function(options){
47503         this.doAction('submit', options);
47504         return this;
47505     },
47506
47507     /**
47508      * Shortcut to do a load action.
47509      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47510      * @return {BasicForm} this
47511      */
47512     load : function(options){
47513         this.doAction('load', options);
47514         return this;
47515     },
47516
47517     /**
47518      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47519      * @param {Record} record The record to edit
47520      * @return {BasicForm} this
47521      */
47522     updateRecord : function(record){
47523         record.beginEdit();
47524         var fs = record.fields;
47525         fs.each(function(f){
47526             var field = this.findField(f.name);
47527             if(field){
47528                 record.set(f.name, field.getValue());
47529             }
47530         }, this);
47531         record.endEdit();
47532         return this;
47533     },
47534
47535     /**
47536      * Loads an Roo.data.Record into this form.
47537      * @param {Record} record The record to load
47538      * @return {BasicForm} this
47539      */
47540     loadRecord : function(record){
47541         this.setValues(record.data);
47542         return this;
47543     },
47544
47545     // private
47546     beforeAction : function(action){
47547         var o = action.options;
47548         
47549         if(!this.disableMask) {
47550             if(this.waitMsgTarget === true){
47551                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47552             }else if(this.waitMsgTarget){
47553                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47554                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47555             }else {
47556                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47557             }
47558         }
47559         
47560          
47561     },
47562
47563     // private
47564     afterAction : function(action, success){
47565         this.activeAction = null;
47566         var o = action.options;
47567         
47568         if(!this.disableMask) {
47569             if(this.waitMsgTarget === true){
47570                 this.el.unmask();
47571             }else if(this.waitMsgTarget){
47572                 this.waitMsgTarget.unmask();
47573             }else{
47574                 Roo.MessageBox.updateProgress(1);
47575                 Roo.MessageBox.hide();
47576             }
47577         }
47578         
47579         if(success){
47580             if(o.reset){
47581                 this.reset();
47582             }
47583             Roo.callback(o.success, o.scope, [this, action]);
47584             this.fireEvent('actioncomplete', this, action);
47585             
47586         }else{
47587             
47588             // failure condition..
47589             // we have a scenario where updates need confirming.
47590             // eg. if a locking scenario exists..
47591             // we look for { errors : { needs_confirm : true }} in the response.
47592             if (
47593                 (typeof(action.result) != 'undefined')  &&
47594                 (typeof(action.result.errors) != 'undefined')  &&
47595                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47596            ){
47597                 var _t = this;
47598                 Roo.MessageBox.confirm(
47599                     "Change requires confirmation",
47600                     action.result.errorMsg,
47601                     function(r) {
47602                         if (r != 'yes') {
47603                             return;
47604                         }
47605                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47606                     }
47607                     
47608                 );
47609                 
47610                 
47611                 
47612                 return;
47613             }
47614             
47615             Roo.callback(o.failure, o.scope, [this, action]);
47616             // show an error message if no failed handler is set..
47617             if (!this.hasListener('actionfailed')) {
47618                 Roo.MessageBox.alert("Error",
47619                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47620                         action.result.errorMsg :
47621                         "Saving Failed, please check your entries or try again"
47622                 );
47623             }
47624             
47625             this.fireEvent('actionfailed', this, action);
47626         }
47627         
47628     },
47629
47630     /**
47631      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47632      * @param {String} id The value to search for
47633      * @return Field
47634      */
47635     findField : function(id){
47636         var field = this.items.get(id);
47637         if(!field){
47638             this.items.each(function(f){
47639                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47640                     field = f;
47641                     return false;
47642                 }
47643             });
47644         }
47645         return field || null;
47646     },
47647
47648     /**
47649      * Add a secondary form to this one, 
47650      * Used to provide tabbed forms. One form is primary, with hidden values 
47651      * which mirror the elements from the other forms.
47652      * 
47653      * @param {Roo.form.Form} form to add.
47654      * 
47655      */
47656     addForm : function(form)
47657     {
47658        
47659         if (this.childForms.indexOf(form) > -1) {
47660             // already added..
47661             return;
47662         }
47663         this.childForms.push(form);
47664         var n = '';
47665         Roo.each(form.allItems, function (fe) {
47666             
47667             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47668             if (this.findField(n)) { // already added..
47669                 return;
47670             }
47671             var add = new Roo.form.Hidden({
47672                 name : n
47673             });
47674             add.render(this.el);
47675             
47676             this.add( add );
47677         }, this);
47678         
47679     },
47680     /**
47681      * Mark fields in this form invalid in bulk.
47682      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47683      * @return {BasicForm} this
47684      */
47685     markInvalid : function(errors){
47686         if(errors instanceof Array){
47687             for(var i = 0, len = errors.length; i < len; i++){
47688                 var fieldError = errors[i];
47689                 var f = this.findField(fieldError.id);
47690                 if(f){
47691                     f.markInvalid(fieldError.msg);
47692                 }
47693             }
47694         }else{
47695             var field, id;
47696             for(id in errors){
47697                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47698                     field.markInvalid(errors[id]);
47699                 }
47700             }
47701         }
47702         Roo.each(this.childForms || [], function (f) {
47703             f.markInvalid(errors);
47704         });
47705         
47706         return this;
47707     },
47708
47709     /**
47710      * Set values for fields in this form in bulk.
47711      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47712      * @return {BasicForm} this
47713      */
47714     setValues : function(values){
47715         if(values instanceof Array){ // array of objects
47716             for(var i = 0, len = values.length; i < len; i++){
47717                 var v = values[i];
47718                 var f = this.findField(v.id);
47719                 if(f){
47720                     f.setValue(v.value);
47721                     if(this.trackResetOnLoad){
47722                         f.originalValue = f.getValue();
47723                     }
47724                 }
47725             }
47726         }else{ // object hash
47727             var field, id;
47728             for(id in values){
47729                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47730                     
47731                     if (field.setFromData && 
47732                         field.valueField && 
47733                         field.displayField &&
47734                         // combos' with local stores can 
47735                         // be queried via setValue()
47736                         // to set their value..
47737                         (field.store && !field.store.isLocal)
47738                         ) {
47739                         // it's a combo
47740                         var sd = { };
47741                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47742                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47743                         field.setFromData(sd);
47744                         
47745                     } else {
47746                         field.setValue(values[id]);
47747                     }
47748                     
47749                     
47750                     if(this.trackResetOnLoad){
47751                         field.originalValue = field.getValue();
47752                     }
47753                 }
47754             }
47755         }
47756         this.resetHasChanged();
47757         
47758         
47759         Roo.each(this.childForms || [], function (f) {
47760             f.setValues(values);
47761             f.resetHasChanged();
47762         });
47763                 
47764         return this;
47765     },
47766  
47767     /**
47768      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47769      * they are returned as an array.
47770      * @param {Boolean} asString
47771      * @return {Object}
47772      */
47773     getValues : function(asString){
47774         if (this.childForms) {
47775             // copy values from the child forms
47776             Roo.each(this.childForms, function (f) {
47777                 this.setValues(f.getValues());
47778             }, this);
47779         }
47780         
47781         // use formdata
47782         if (typeof(FormData) != 'undefined' && asString !== true) {
47783             var fd = (new FormData(this.el.dom)).entries();
47784             var ret = {};
47785             var ent = fd.next();
47786             while (!ent.done) {
47787                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47788                 ent = fd.next();
47789             };
47790             return ret;
47791         }
47792         
47793         
47794         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47795         if(asString === true){
47796             return fs;
47797         }
47798         return Roo.urlDecode(fs);
47799     },
47800     
47801     /**
47802      * Returns the fields in this form as an object with key/value pairs. 
47803      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47804      * @return {Object}
47805      */
47806     getFieldValues : function(with_hidden)
47807     {
47808         if (this.childForms) {
47809             // copy values from the child forms
47810             // should this call getFieldValues - probably not as we do not currently copy
47811             // hidden fields when we generate..
47812             Roo.each(this.childForms, function (f) {
47813                 this.setValues(f.getValues());
47814             }, this);
47815         }
47816         
47817         var ret = {};
47818         this.items.each(function(f){
47819             if (!f.getName()) {
47820                 return;
47821             }
47822             var v = f.getValue();
47823             if (f.inputType =='radio') {
47824                 if (typeof(ret[f.getName()]) == 'undefined') {
47825                     ret[f.getName()] = ''; // empty..
47826                 }
47827                 
47828                 if (!f.el.dom.checked) {
47829                     return;
47830                     
47831                 }
47832                 v = f.el.dom.value;
47833                 
47834             }
47835             
47836             // not sure if this supported any more..
47837             if ((typeof(v) == 'object') && f.getRawValue) {
47838                 v = f.getRawValue() ; // dates..
47839             }
47840             // combo boxes where name != hiddenName...
47841             if (f.name != f.getName()) {
47842                 ret[f.name] = f.getRawValue();
47843             }
47844             ret[f.getName()] = v;
47845         });
47846         
47847         return ret;
47848     },
47849
47850     /**
47851      * Clears all invalid messages in this form.
47852      * @return {BasicForm} this
47853      */
47854     clearInvalid : function(){
47855         this.items.each(function(f){
47856            f.clearInvalid();
47857         });
47858         
47859         Roo.each(this.childForms || [], function (f) {
47860             f.clearInvalid();
47861         });
47862         
47863         
47864         return this;
47865     },
47866
47867     /**
47868      * Resets this form.
47869      * @return {BasicForm} this
47870      */
47871     reset : function(){
47872         this.items.each(function(f){
47873             f.reset();
47874         });
47875         
47876         Roo.each(this.childForms || [], function (f) {
47877             f.reset();
47878         });
47879         this.resetHasChanged();
47880         
47881         return this;
47882     },
47883
47884     /**
47885      * Add Roo.form components to this form.
47886      * @param {Field} field1
47887      * @param {Field} field2 (optional)
47888      * @param {Field} etc (optional)
47889      * @return {BasicForm} this
47890      */
47891     add : function(){
47892         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47893         return this;
47894     },
47895
47896
47897     /**
47898      * Removes a field from the items collection (does NOT remove its markup).
47899      * @param {Field} field
47900      * @return {BasicForm} this
47901      */
47902     remove : function(field){
47903         this.items.remove(field);
47904         return this;
47905     },
47906
47907     /**
47908      * Looks at the fields in this form, checks them for an id attribute,
47909      * and calls applyTo on the existing dom element with that id.
47910      * @return {BasicForm} this
47911      */
47912     render : function(){
47913         this.items.each(function(f){
47914             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47915                 f.applyTo(f.id);
47916             }
47917         });
47918         return this;
47919     },
47920
47921     /**
47922      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47923      * @param {Object} values
47924      * @return {BasicForm} this
47925      */
47926     applyToFields : function(o){
47927         this.items.each(function(f){
47928            Roo.apply(f, o);
47929         });
47930         return this;
47931     },
47932
47933     /**
47934      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47935      * @param {Object} values
47936      * @return {BasicForm} this
47937      */
47938     applyIfToFields : function(o){
47939         this.items.each(function(f){
47940            Roo.applyIf(f, o);
47941         });
47942         return this;
47943     }
47944 });
47945
47946 // back compat
47947 Roo.BasicForm = Roo.form.BasicForm;
47948
47949 Roo.apply(Roo.form.BasicForm, {
47950     
47951     popover : {
47952         
47953         padding : 5,
47954         
47955         isApplied : false,
47956         
47957         isMasked : false,
47958         
47959         form : false,
47960         
47961         target : false,
47962         
47963         intervalID : false,
47964         
47965         maskEl : false,
47966         
47967         apply : function()
47968         {
47969             if(this.isApplied){
47970                 return;
47971             }
47972             
47973             this.maskEl = {
47974                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
47975                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
47976                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
47977                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
47978             };
47979             
47980             this.maskEl.top.enableDisplayMode("block");
47981             this.maskEl.left.enableDisplayMode("block");
47982             this.maskEl.bottom.enableDisplayMode("block");
47983             this.maskEl.right.enableDisplayMode("block");
47984             
47985             Roo.get(document.body).on('click', function(){
47986                 this.unmask();
47987             }, this);
47988             
47989             Roo.get(document.body).on('touchstart', function(){
47990                 this.unmask();
47991             }, this);
47992             
47993             this.isApplied = true
47994         },
47995         
47996         mask : function(form, target)
47997         {
47998             this.form = form;
47999             
48000             this.target = target;
48001             
48002             if(!this.form.errorMask || !target.el){
48003                 return;
48004             }
48005             
48006             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48007             
48008             var ot = this.target.el.calcOffsetsTo(scrollable);
48009             
48010             var scrollTo = ot[1] - this.form.maskOffset;
48011             
48012             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48013             
48014             scrollable.scrollTo('top', scrollTo);
48015             
48016             var el = this.target.wrap || this.target.el;
48017             
48018             var box = el.getBox();
48019             
48020             this.maskEl.top.setStyle('position', 'absolute');
48021             this.maskEl.top.setStyle('z-index', 10000);
48022             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48023             this.maskEl.top.setLeft(0);
48024             this.maskEl.top.setTop(0);
48025             this.maskEl.top.show();
48026             
48027             this.maskEl.left.setStyle('position', 'absolute');
48028             this.maskEl.left.setStyle('z-index', 10000);
48029             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48030             this.maskEl.left.setLeft(0);
48031             this.maskEl.left.setTop(box.y - this.padding);
48032             this.maskEl.left.show();
48033
48034             this.maskEl.bottom.setStyle('position', 'absolute');
48035             this.maskEl.bottom.setStyle('z-index', 10000);
48036             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48037             this.maskEl.bottom.setLeft(0);
48038             this.maskEl.bottom.setTop(box.bottom + this.padding);
48039             this.maskEl.bottom.show();
48040
48041             this.maskEl.right.setStyle('position', 'absolute');
48042             this.maskEl.right.setStyle('z-index', 10000);
48043             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48044             this.maskEl.right.setLeft(box.right + this.padding);
48045             this.maskEl.right.setTop(box.y - this.padding);
48046             this.maskEl.right.show();
48047
48048             this.intervalID = window.setInterval(function() {
48049                 Roo.form.BasicForm.popover.unmask();
48050             }, 10000);
48051
48052             window.onwheel = function(){ return false;};
48053             
48054             (function(){ this.isMasked = true; }).defer(500, this);
48055             
48056         },
48057         
48058         unmask : function()
48059         {
48060             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48061                 return;
48062             }
48063             
48064             this.maskEl.top.setStyle('position', 'absolute');
48065             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48066             this.maskEl.top.hide();
48067
48068             this.maskEl.left.setStyle('position', 'absolute');
48069             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48070             this.maskEl.left.hide();
48071
48072             this.maskEl.bottom.setStyle('position', 'absolute');
48073             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48074             this.maskEl.bottom.hide();
48075
48076             this.maskEl.right.setStyle('position', 'absolute');
48077             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48078             this.maskEl.right.hide();
48079             
48080             window.onwheel = function(){ return true;};
48081             
48082             if(this.intervalID){
48083                 window.clearInterval(this.intervalID);
48084                 this.intervalID = false;
48085             }
48086             
48087             this.isMasked = false;
48088             
48089         }
48090         
48091     }
48092     
48093 });/*
48094  * Based on:
48095  * Ext JS Library 1.1.1
48096  * Copyright(c) 2006-2007, Ext JS, LLC.
48097  *
48098  * Originally Released Under LGPL - original licence link has changed is not relivant.
48099  *
48100  * Fork - LGPL
48101  * <script type="text/javascript">
48102  */
48103
48104 /**
48105  * @class Roo.form.Form
48106  * @extends Roo.form.BasicForm
48107  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48108  * @constructor
48109  * @param {Object} config Configuration options
48110  */
48111 Roo.form.Form = function(config){
48112     var xitems =  [];
48113     if (config.items) {
48114         xitems = config.items;
48115         delete config.items;
48116     }
48117    
48118     
48119     Roo.form.Form.superclass.constructor.call(this, null, config);
48120     this.url = this.url || this.action;
48121     if(!this.root){
48122         this.root = new Roo.form.Layout(Roo.applyIf({
48123             id: Roo.id()
48124         }, config));
48125     }
48126     this.active = this.root;
48127     /**
48128      * Array of all the buttons that have been added to this form via {@link addButton}
48129      * @type Array
48130      */
48131     this.buttons = [];
48132     this.allItems = [];
48133     this.addEvents({
48134         /**
48135          * @event clientvalidation
48136          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48137          * @param {Form} this
48138          * @param {Boolean} valid true if the form has passed client-side validation
48139          */
48140         clientvalidation: true,
48141         /**
48142          * @event rendered
48143          * Fires when the form is rendered
48144          * @param {Roo.form.Form} form
48145          */
48146         rendered : true
48147     });
48148     
48149     if (this.progressUrl) {
48150             // push a hidden field onto the list of fields..
48151             this.addxtype( {
48152                     xns: Roo.form, 
48153                     xtype : 'Hidden', 
48154                     name : 'UPLOAD_IDENTIFIER' 
48155             });
48156         }
48157         
48158     
48159     Roo.each(xitems, this.addxtype, this);
48160     
48161 };
48162
48163 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48164     /**
48165      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48166      */
48167     /**
48168      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48169      */
48170     /**
48171      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48172      */
48173     buttonAlign:'center',
48174
48175     /**
48176      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48177      */
48178     minButtonWidth:75,
48179
48180     /**
48181      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48182      * This property cascades to child containers if not set.
48183      */
48184     labelAlign:'left',
48185
48186     /**
48187      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48188      * fires a looping event with that state. This is required to bind buttons to the valid
48189      * state using the config value formBind:true on the button.
48190      */
48191     monitorValid : false,
48192
48193     /**
48194      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48195      */
48196     monitorPoll : 200,
48197     
48198     /**
48199      * @cfg {String} progressUrl - Url to return progress data 
48200      */
48201     
48202     progressUrl : false,
48203     /**
48204      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48205      * sending a formdata with extra parameters - eg uploaded elements.
48206      */
48207     
48208     formData : false,
48209     
48210     /**
48211      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48212      * fields are added and the column is closed. If no fields are passed the column remains open
48213      * until end() is called.
48214      * @param {Object} config The config to pass to the column
48215      * @param {Field} field1 (optional)
48216      * @param {Field} field2 (optional)
48217      * @param {Field} etc (optional)
48218      * @return Column The column container object
48219      */
48220     column : function(c){
48221         var col = new Roo.form.Column(c);
48222         this.start(col);
48223         if(arguments.length > 1){ // duplicate code required because of Opera
48224             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48225             this.end();
48226         }
48227         return col;
48228     },
48229
48230     /**
48231      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48232      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48233      * until end() is called.
48234      * @param {Object} config The config to pass to the fieldset
48235      * @param {Field} field1 (optional)
48236      * @param {Field} field2 (optional)
48237      * @param {Field} etc (optional)
48238      * @return FieldSet The fieldset container object
48239      */
48240     fieldset : function(c){
48241         var fs = new Roo.form.FieldSet(c);
48242         this.start(fs);
48243         if(arguments.length > 1){ // duplicate code required because of Opera
48244             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48245             this.end();
48246         }
48247         return fs;
48248     },
48249
48250     /**
48251      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48252      * fields are added and the container is closed. If no fields are passed the container remains open
48253      * until end() is called.
48254      * @param {Object} config The config to pass to the Layout
48255      * @param {Field} field1 (optional)
48256      * @param {Field} field2 (optional)
48257      * @param {Field} etc (optional)
48258      * @return Layout The container object
48259      */
48260     container : function(c){
48261         var l = new Roo.form.Layout(c);
48262         this.start(l);
48263         if(arguments.length > 1){ // duplicate code required because of Opera
48264             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48265             this.end();
48266         }
48267         return l;
48268     },
48269
48270     /**
48271      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48272      * @param {Object} container A Roo.form.Layout or subclass of Layout
48273      * @return {Form} this
48274      */
48275     start : function(c){
48276         // cascade label info
48277         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48278         this.active.stack.push(c);
48279         c.ownerCt = this.active;
48280         this.active = c;
48281         return this;
48282     },
48283
48284     /**
48285      * Closes the current open container
48286      * @return {Form} this
48287      */
48288     end : function(){
48289         if(this.active == this.root){
48290             return this;
48291         }
48292         this.active = this.active.ownerCt;
48293         return this;
48294     },
48295
48296     /**
48297      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48298      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48299      * as the label of the field.
48300      * @param {Field} field1
48301      * @param {Field} field2 (optional)
48302      * @param {Field} etc. (optional)
48303      * @return {Form} this
48304      */
48305     add : function(){
48306         this.active.stack.push.apply(this.active.stack, arguments);
48307         this.allItems.push.apply(this.allItems,arguments);
48308         var r = [];
48309         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48310             if(a[i].isFormField){
48311                 r.push(a[i]);
48312             }
48313         }
48314         if(r.length > 0){
48315             Roo.form.Form.superclass.add.apply(this, r);
48316         }
48317         return this;
48318     },
48319     
48320
48321     
48322     
48323     
48324      /**
48325      * Find any element that has been added to a form, using it's ID or name
48326      * This can include framesets, columns etc. along with regular fields..
48327      * @param {String} id - id or name to find.
48328      
48329      * @return {Element} e - or false if nothing found.
48330      */
48331     findbyId : function(id)
48332     {
48333         var ret = false;
48334         if (!id) {
48335             return ret;
48336         }
48337         Roo.each(this.allItems, function(f){
48338             if (f.id == id || f.name == id ){
48339                 ret = f;
48340                 return false;
48341             }
48342         });
48343         return ret;
48344     },
48345
48346     
48347     
48348     /**
48349      * Render this form into the passed container. This should only be called once!
48350      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48351      * @return {Form} this
48352      */
48353     render : function(ct)
48354     {
48355         
48356         
48357         
48358         ct = Roo.get(ct);
48359         var o = this.autoCreate || {
48360             tag: 'form',
48361             method : this.method || 'POST',
48362             id : this.id || Roo.id()
48363         };
48364         this.initEl(ct.createChild(o));
48365
48366         this.root.render(this.el);
48367         
48368        
48369              
48370         this.items.each(function(f){
48371             f.render('x-form-el-'+f.id);
48372         });
48373
48374         if(this.buttons.length > 0){
48375             // tables are required to maintain order and for correct IE layout
48376             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48377                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48378                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48379             }}, null, true);
48380             var tr = tb.getElementsByTagName('tr')[0];
48381             for(var i = 0, len = this.buttons.length; i < len; i++) {
48382                 var b = this.buttons[i];
48383                 var td = document.createElement('td');
48384                 td.className = 'x-form-btn-td';
48385                 b.render(tr.appendChild(td));
48386             }
48387         }
48388         if(this.monitorValid){ // initialize after render
48389             this.startMonitoring();
48390         }
48391         this.fireEvent('rendered', this);
48392         return this;
48393     },
48394
48395     /**
48396      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48397      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48398      * object or a valid Roo.DomHelper element config
48399      * @param {Function} handler The function called when the button is clicked
48400      * @param {Object} scope (optional) The scope of the handler function
48401      * @return {Roo.Button}
48402      */
48403     addButton : function(config, handler, scope){
48404         var bc = {
48405             handler: handler,
48406             scope: scope,
48407             minWidth: this.minButtonWidth,
48408             hideParent:true
48409         };
48410         if(typeof config == "string"){
48411             bc.text = config;
48412         }else{
48413             Roo.apply(bc, config);
48414         }
48415         var btn = new Roo.Button(null, bc);
48416         this.buttons.push(btn);
48417         return btn;
48418     },
48419
48420      /**
48421      * Adds a series of form elements (using the xtype property as the factory method.
48422      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48423      * @param {Object} config 
48424      */
48425     
48426     addxtype : function()
48427     {
48428         var ar = Array.prototype.slice.call(arguments, 0);
48429         var ret = false;
48430         for(var i = 0; i < ar.length; i++) {
48431             if (!ar[i]) {
48432                 continue; // skip -- if this happends something invalid got sent, we 
48433                 // should ignore it, as basically that interface element will not show up
48434                 // and that should be pretty obvious!!
48435             }
48436             
48437             if (Roo.form[ar[i].xtype]) {
48438                 ar[i].form = this;
48439                 var fe = Roo.factory(ar[i], Roo.form);
48440                 if (!ret) {
48441                     ret = fe;
48442                 }
48443                 fe.form = this;
48444                 if (fe.store) {
48445                     fe.store.form = this;
48446                 }
48447                 if (fe.isLayout) {  
48448                          
48449                     this.start(fe);
48450                     this.allItems.push(fe);
48451                     if (fe.items && fe.addxtype) {
48452                         fe.addxtype.apply(fe, fe.items);
48453                         delete fe.items;
48454                     }
48455                      this.end();
48456                     continue;
48457                 }
48458                 
48459                 
48460                  
48461                 this.add(fe);
48462               //  console.log('adding ' + ar[i].xtype);
48463             }
48464             if (ar[i].xtype == 'Button') {  
48465                 //console.log('adding button');
48466                 //console.log(ar[i]);
48467                 this.addButton(ar[i]);
48468                 this.allItems.push(fe);
48469                 continue;
48470             }
48471             
48472             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48473                 alert('end is not supported on xtype any more, use items');
48474             //    this.end();
48475             //    //console.log('adding end');
48476             }
48477             
48478         }
48479         return ret;
48480     },
48481     
48482     /**
48483      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48484      * option "monitorValid"
48485      */
48486     startMonitoring : function(){
48487         if(!this.bound){
48488             this.bound = true;
48489             Roo.TaskMgr.start({
48490                 run : this.bindHandler,
48491                 interval : this.monitorPoll || 200,
48492                 scope: this
48493             });
48494         }
48495     },
48496
48497     /**
48498      * Stops monitoring of the valid state of this form
48499      */
48500     stopMonitoring : function(){
48501         this.bound = false;
48502     },
48503
48504     // private
48505     bindHandler : function(){
48506         if(!this.bound){
48507             return false; // stops binding
48508         }
48509         var valid = true;
48510         this.items.each(function(f){
48511             if(!f.isValid(true)){
48512                 valid = false;
48513                 return false;
48514             }
48515         });
48516         for(var i = 0, len = this.buttons.length; i < len; i++){
48517             var btn = this.buttons[i];
48518             if(btn.formBind === true && btn.disabled === valid){
48519                 btn.setDisabled(!valid);
48520             }
48521         }
48522         this.fireEvent('clientvalidation', this, valid);
48523     }
48524     
48525     
48526     
48527     
48528     
48529     
48530     
48531     
48532 });
48533
48534
48535 // back compat
48536 Roo.Form = Roo.form.Form;
48537 /*
48538  * Based on:
48539  * Ext JS Library 1.1.1
48540  * Copyright(c) 2006-2007, Ext JS, LLC.
48541  *
48542  * Originally Released Under LGPL - original licence link has changed is not relivant.
48543  *
48544  * Fork - LGPL
48545  * <script type="text/javascript">
48546  */
48547
48548 // as we use this in bootstrap.
48549 Roo.namespace('Roo.form');
48550  /**
48551  * @class Roo.form.Action
48552  * Internal Class used to handle form actions
48553  * @constructor
48554  * @param {Roo.form.BasicForm} el The form element or its id
48555  * @param {Object} config Configuration options
48556  */
48557
48558  
48559  
48560 // define the action interface
48561 Roo.form.Action = function(form, options){
48562     this.form = form;
48563     this.options = options || {};
48564 };
48565 /**
48566  * Client Validation Failed
48567  * @const 
48568  */
48569 Roo.form.Action.CLIENT_INVALID = 'client';
48570 /**
48571  * Server Validation Failed
48572  * @const 
48573  */
48574 Roo.form.Action.SERVER_INVALID = 'server';
48575  /**
48576  * Connect to Server Failed
48577  * @const 
48578  */
48579 Roo.form.Action.CONNECT_FAILURE = 'connect';
48580 /**
48581  * Reading Data from Server Failed
48582  * @const 
48583  */
48584 Roo.form.Action.LOAD_FAILURE = 'load';
48585
48586 Roo.form.Action.prototype = {
48587     type : 'default',
48588     failureType : undefined,
48589     response : undefined,
48590     result : undefined,
48591
48592     // interface method
48593     run : function(options){
48594
48595     },
48596
48597     // interface method
48598     success : function(response){
48599
48600     },
48601
48602     // interface method
48603     handleResponse : function(response){
48604
48605     },
48606
48607     // default connection failure
48608     failure : function(response){
48609         
48610         this.response = response;
48611         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48612         this.form.afterAction(this, false);
48613     },
48614
48615     processResponse : function(response){
48616         this.response = response;
48617         if(!response.responseText){
48618             return true;
48619         }
48620         this.result = this.handleResponse(response);
48621         return this.result;
48622     },
48623
48624     // utility functions used internally
48625     getUrl : function(appendParams){
48626         var url = this.options.url || this.form.url || this.form.el.dom.action;
48627         if(appendParams){
48628             var p = this.getParams();
48629             if(p){
48630                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48631             }
48632         }
48633         return url;
48634     },
48635
48636     getMethod : function(){
48637         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48638     },
48639
48640     getParams : function(){
48641         var bp = this.form.baseParams;
48642         var p = this.options.params;
48643         if(p){
48644             if(typeof p == "object"){
48645                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48646             }else if(typeof p == 'string' && bp){
48647                 p += '&' + Roo.urlEncode(bp);
48648             }
48649         }else if(bp){
48650             p = Roo.urlEncode(bp);
48651         }
48652         return p;
48653     },
48654
48655     createCallback : function(){
48656         return {
48657             success: this.success,
48658             failure: this.failure,
48659             scope: this,
48660             timeout: (this.form.timeout*1000),
48661             upload: this.form.fileUpload ? this.success : undefined
48662         };
48663     }
48664 };
48665
48666 Roo.form.Action.Submit = function(form, options){
48667     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48668 };
48669
48670 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48671     type : 'submit',
48672
48673     haveProgress : false,
48674     uploadComplete : false,
48675     
48676     // uploadProgress indicator.
48677     uploadProgress : function()
48678     {
48679         if (!this.form.progressUrl) {
48680             return;
48681         }
48682         
48683         if (!this.haveProgress) {
48684             Roo.MessageBox.progress("Uploading", "Uploading");
48685         }
48686         if (this.uploadComplete) {
48687            Roo.MessageBox.hide();
48688            return;
48689         }
48690         
48691         this.haveProgress = true;
48692    
48693         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48694         
48695         var c = new Roo.data.Connection();
48696         c.request({
48697             url : this.form.progressUrl,
48698             params: {
48699                 id : uid
48700             },
48701             method: 'GET',
48702             success : function(req){
48703                //console.log(data);
48704                 var rdata = false;
48705                 var edata;
48706                 try  {
48707                    rdata = Roo.decode(req.responseText)
48708                 } catch (e) {
48709                     Roo.log("Invalid data from server..");
48710                     Roo.log(edata);
48711                     return;
48712                 }
48713                 if (!rdata || !rdata.success) {
48714                     Roo.log(rdata);
48715                     Roo.MessageBox.alert(Roo.encode(rdata));
48716                     return;
48717                 }
48718                 var data = rdata.data;
48719                 
48720                 if (this.uploadComplete) {
48721                    Roo.MessageBox.hide();
48722                    return;
48723                 }
48724                    
48725                 if (data){
48726                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48727                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48728                     );
48729                 }
48730                 this.uploadProgress.defer(2000,this);
48731             },
48732        
48733             failure: function(data) {
48734                 Roo.log('progress url failed ');
48735                 Roo.log(data);
48736             },
48737             scope : this
48738         });
48739            
48740     },
48741     
48742     
48743     run : function()
48744     {
48745         // run get Values on the form, so it syncs any secondary forms.
48746         this.form.getValues();
48747         
48748         var o = this.options;
48749         var method = this.getMethod();
48750         var isPost = method == 'POST';
48751         if(o.clientValidation === false || this.form.isValid()){
48752             
48753             if (this.form.progressUrl) {
48754                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48755                     (new Date() * 1) + '' + Math.random());
48756                     
48757             } 
48758             
48759             
48760             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48761                 form:this.form.el.dom,
48762                 url:this.getUrl(!isPost),
48763                 method: method,
48764                 params:isPost ? this.getParams() : null,
48765                 isUpload: this.form.fileUpload,
48766                 formData : this.form.formData
48767             }));
48768             
48769             this.uploadProgress();
48770
48771         }else if (o.clientValidation !== false){ // client validation failed
48772             this.failureType = Roo.form.Action.CLIENT_INVALID;
48773             this.form.afterAction(this, false);
48774         }
48775     },
48776
48777     success : function(response)
48778     {
48779         this.uploadComplete= true;
48780         if (this.haveProgress) {
48781             Roo.MessageBox.hide();
48782         }
48783         
48784         
48785         var result = this.processResponse(response);
48786         if(result === true || result.success){
48787             this.form.afterAction(this, true);
48788             return;
48789         }
48790         if(result.errors){
48791             this.form.markInvalid(result.errors);
48792             this.failureType = Roo.form.Action.SERVER_INVALID;
48793         }
48794         this.form.afterAction(this, false);
48795     },
48796     failure : function(response)
48797     {
48798         this.uploadComplete= true;
48799         if (this.haveProgress) {
48800             Roo.MessageBox.hide();
48801         }
48802         
48803         this.response = response;
48804         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48805         this.form.afterAction(this, false);
48806     },
48807     
48808     handleResponse : function(response){
48809         if(this.form.errorReader){
48810             var rs = this.form.errorReader.read(response);
48811             var errors = [];
48812             if(rs.records){
48813                 for(var i = 0, len = rs.records.length; i < len; i++) {
48814                     var r = rs.records[i];
48815                     errors[i] = r.data;
48816                 }
48817             }
48818             if(errors.length < 1){
48819                 errors = null;
48820             }
48821             return {
48822                 success : rs.success,
48823                 errors : errors
48824             };
48825         }
48826         var ret = false;
48827         try {
48828             ret = Roo.decode(response.responseText);
48829         } catch (e) {
48830             ret = {
48831                 success: false,
48832                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48833                 errors : []
48834             };
48835         }
48836         return ret;
48837         
48838     }
48839 });
48840
48841
48842 Roo.form.Action.Load = function(form, options){
48843     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48844     this.reader = this.form.reader;
48845 };
48846
48847 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48848     type : 'load',
48849
48850     run : function(){
48851         
48852         Roo.Ajax.request(Roo.apply(
48853                 this.createCallback(), {
48854                     method:this.getMethod(),
48855                     url:this.getUrl(false),
48856                     params:this.getParams()
48857         }));
48858     },
48859
48860     success : function(response){
48861         
48862         var result = this.processResponse(response);
48863         if(result === true || !result.success || !result.data){
48864             this.failureType = Roo.form.Action.LOAD_FAILURE;
48865             this.form.afterAction(this, false);
48866             return;
48867         }
48868         this.form.clearInvalid();
48869         this.form.setValues(result.data);
48870         this.form.afterAction(this, true);
48871     },
48872
48873     handleResponse : function(response){
48874         if(this.form.reader){
48875             var rs = this.form.reader.read(response);
48876             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48877             return {
48878                 success : rs.success,
48879                 data : data
48880             };
48881         }
48882         return Roo.decode(response.responseText);
48883     }
48884 });
48885
48886 Roo.form.Action.ACTION_TYPES = {
48887     'load' : Roo.form.Action.Load,
48888     'submit' : Roo.form.Action.Submit
48889 };/*
48890  * Based on:
48891  * Ext JS Library 1.1.1
48892  * Copyright(c) 2006-2007, Ext JS, LLC.
48893  *
48894  * Originally Released Under LGPL - original licence link has changed is not relivant.
48895  *
48896  * Fork - LGPL
48897  * <script type="text/javascript">
48898  */
48899  
48900 /**
48901  * @class Roo.form.Layout
48902  * @extends Roo.Component
48903  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48904  * @constructor
48905  * @param {Object} config Configuration options
48906  */
48907 Roo.form.Layout = function(config){
48908     var xitems = [];
48909     if (config.items) {
48910         xitems = config.items;
48911         delete config.items;
48912     }
48913     Roo.form.Layout.superclass.constructor.call(this, config);
48914     this.stack = [];
48915     Roo.each(xitems, this.addxtype, this);
48916      
48917 };
48918
48919 Roo.extend(Roo.form.Layout, Roo.Component, {
48920     /**
48921      * @cfg {String/Object} autoCreate
48922      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48923      */
48924     /**
48925      * @cfg {String/Object/Function} style
48926      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48927      * a function which returns such a specification.
48928      */
48929     /**
48930      * @cfg {String} labelAlign
48931      * Valid values are "left," "top" and "right" (defaults to "left")
48932      */
48933     /**
48934      * @cfg {Number} labelWidth
48935      * Fixed width in pixels of all field labels (defaults to undefined)
48936      */
48937     /**
48938      * @cfg {Boolean} clear
48939      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48940      */
48941     clear : true,
48942     /**
48943      * @cfg {String} labelSeparator
48944      * The separator to use after field labels (defaults to ':')
48945      */
48946     labelSeparator : ':',
48947     /**
48948      * @cfg {Boolean} hideLabels
48949      * True to suppress the display of field labels in this layout (defaults to false)
48950      */
48951     hideLabels : false,
48952
48953     // private
48954     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48955     
48956     isLayout : true,
48957     
48958     // private
48959     onRender : function(ct, position){
48960         if(this.el){ // from markup
48961             this.el = Roo.get(this.el);
48962         }else {  // generate
48963             var cfg = this.getAutoCreate();
48964             this.el = ct.createChild(cfg, position);
48965         }
48966         if(this.style){
48967             this.el.applyStyles(this.style);
48968         }
48969         if(this.labelAlign){
48970             this.el.addClass('x-form-label-'+this.labelAlign);
48971         }
48972         if(this.hideLabels){
48973             this.labelStyle = "display:none";
48974             this.elementStyle = "padding-left:0;";
48975         }else{
48976             if(typeof this.labelWidth == 'number'){
48977                 this.labelStyle = "width:"+this.labelWidth+"px;";
48978                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48979             }
48980             if(this.labelAlign == 'top'){
48981                 this.labelStyle = "width:auto;";
48982                 this.elementStyle = "padding-left:0;";
48983             }
48984         }
48985         var stack = this.stack;
48986         var slen = stack.length;
48987         if(slen > 0){
48988             if(!this.fieldTpl){
48989                 var t = new Roo.Template(
48990                     '<div class="x-form-item {5}">',
48991                         '<label for="{0}" style="{2}">{1}{4}</label>',
48992                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48993                         '</div>',
48994                     '</div><div class="x-form-clear-left"></div>'
48995                 );
48996                 t.disableFormats = true;
48997                 t.compile();
48998                 Roo.form.Layout.prototype.fieldTpl = t;
48999             }
49000             for(var i = 0; i < slen; i++) {
49001                 if(stack[i].isFormField){
49002                     this.renderField(stack[i]);
49003                 }else{
49004                     this.renderComponent(stack[i]);
49005                 }
49006             }
49007         }
49008         if(this.clear){
49009             this.el.createChild({cls:'x-form-clear'});
49010         }
49011     },
49012
49013     // private
49014     renderField : function(f){
49015         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49016                f.id, //0
49017                f.fieldLabel, //1
49018                f.labelStyle||this.labelStyle||'', //2
49019                this.elementStyle||'', //3
49020                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49021                f.itemCls||this.itemCls||''  //5
49022        ], true).getPrevSibling());
49023     },
49024
49025     // private
49026     renderComponent : function(c){
49027         c.render(c.isLayout ? this.el : this.el.createChild());    
49028     },
49029     /**
49030      * Adds a object form elements (using the xtype property as the factory method.)
49031      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49032      * @param {Object} config 
49033      */
49034     addxtype : function(o)
49035     {
49036         // create the lement.
49037         o.form = this.form;
49038         var fe = Roo.factory(o, Roo.form);
49039         this.form.allItems.push(fe);
49040         this.stack.push(fe);
49041         
49042         if (fe.isFormField) {
49043             this.form.items.add(fe);
49044         }
49045          
49046         return fe;
49047     }
49048 });
49049
49050 /**
49051  * @class Roo.form.Column
49052  * @extends Roo.form.Layout
49053  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49054  * @constructor
49055  * @param {Object} config Configuration options
49056  */
49057 Roo.form.Column = function(config){
49058     Roo.form.Column.superclass.constructor.call(this, config);
49059 };
49060
49061 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49062     /**
49063      * @cfg {Number/String} width
49064      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49065      */
49066     /**
49067      * @cfg {String/Object} autoCreate
49068      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49069      */
49070
49071     // private
49072     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49073
49074     // private
49075     onRender : function(ct, position){
49076         Roo.form.Column.superclass.onRender.call(this, ct, position);
49077         if(this.width){
49078             this.el.setWidth(this.width);
49079         }
49080     }
49081 });
49082
49083
49084 /**
49085  * @class Roo.form.Row
49086  * @extends Roo.form.Layout
49087  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49088  * @constructor
49089  * @param {Object} config Configuration options
49090  */
49091
49092  
49093 Roo.form.Row = function(config){
49094     Roo.form.Row.superclass.constructor.call(this, config);
49095 };
49096  
49097 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49098       /**
49099      * @cfg {Number/String} width
49100      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49101      */
49102     /**
49103      * @cfg {Number/String} height
49104      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49105      */
49106     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49107     
49108     padWidth : 20,
49109     // private
49110     onRender : function(ct, position){
49111         //console.log('row render');
49112         if(!this.rowTpl){
49113             var t = new Roo.Template(
49114                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49115                     '<label for="{0}" style="{2}">{1}{4}</label>',
49116                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49117                     '</div>',
49118                 '</div>'
49119             );
49120             t.disableFormats = true;
49121             t.compile();
49122             Roo.form.Layout.prototype.rowTpl = t;
49123         }
49124         this.fieldTpl = this.rowTpl;
49125         
49126         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49127         var labelWidth = 100;
49128         
49129         if ((this.labelAlign != 'top')) {
49130             if (typeof this.labelWidth == 'number') {
49131                 labelWidth = this.labelWidth
49132             }
49133             this.padWidth =  20 + labelWidth;
49134             
49135         }
49136         
49137         Roo.form.Column.superclass.onRender.call(this, ct, position);
49138         if(this.width){
49139             this.el.setWidth(this.width);
49140         }
49141         if(this.height){
49142             this.el.setHeight(this.height);
49143         }
49144     },
49145     
49146     // private
49147     renderField : function(f){
49148         f.fieldEl = this.fieldTpl.append(this.el, [
49149                f.id, f.fieldLabel,
49150                f.labelStyle||this.labelStyle||'',
49151                this.elementStyle||'',
49152                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49153                f.itemCls||this.itemCls||'',
49154                f.width ? f.width + this.padWidth : 160 + this.padWidth
49155        ],true);
49156     }
49157 });
49158  
49159
49160 /**
49161  * @class Roo.form.FieldSet
49162  * @extends Roo.form.Layout
49163  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49164  * @constructor
49165  * @param {Object} config Configuration options
49166  */
49167 Roo.form.FieldSet = function(config){
49168     Roo.form.FieldSet.superclass.constructor.call(this, config);
49169 };
49170
49171 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49172     /**
49173      * @cfg {String} legend
49174      * The text to display as the legend for the FieldSet (defaults to '')
49175      */
49176     /**
49177      * @cfg {String/Object} autoCreate
49178      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49179      */
49180
49181     // private
49182     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49183
49184     // private
49185     onRender : function(ct, position){
49186         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49187         if(this.legend){
49188             this.setLegend(this.legend);
49189         }
49190     },
49191
49192     // private
49193     setLegend : function(text){
49194         if(this.rendered){
49195             this.el.child('legend').update(text);
49196         }
49197     }
49198 });/*
49199  * Based on:
49200  * Ext JS Library 1.1.1
49201  * Copyright(c) 2006-2007, Ext JS, LLC.
49202  *
49203  * Originally Released Under LGPL - original licence link has changed is not relivant.
49204  *
49205  * Fork - LGPL
49206  * <script type="text/javascript">
49207  */
49208 /**
49209  * @class Roo.form.VTypes
49210  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49211  * @singleton
49212  */
49213 Roo.form.VTypes = function(){
49214     // closure these in so they are only created once.
49215     var alpha = /^[a-zA-Z_]+$/;
49216     var alphanum = /^[a-zA-Z0-9_]+$/;
49217     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49218     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49219
49220     // All these messages and functions are configurable
49221     return {
49222         /**
49223          * The function used to validate email addresses
49224          * @param {String} value The email address
49225          */
49226         'email' : function(v){
49227             return email.test(v);
49228         },
49229         /**
49230          * The error text to display when the email validation function returns false
49231          * @type String
49232          */
49233         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49234         /**
49235          * The keystroke filter mask to be applied on email input
49236          * @type RegExp
49237          */
49238         'emailMask' : /[a-z0-9_\.\-@]/i,
49239
49240         /**
49241          * The function used to validate URLs
49242          * @param {String} value The URL
49243          */
49244         'url' : function(v){
49245             return url.test(v);
49246         },
49247         /**
49248          * The error text to display when the url validation function returns false
49249          * @type String
49250          */
49251         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49252         
49253         /**
49254          * The function used to validate alpha values
49255          * @param {String} value The value
49256          */
49257         'alpha' : function(v){
49258             return alpha.test(v);
49259         },
49260         /**
49261          * The error text to display when the alpha validation function returns false
49262          * @type String
49263          */
49264         'alphaText' : 'This field should only contain letters and _',
49265         /**
49266          * The keystroke filter mask to be applied on alpha input
49267          * @type RegExp
49268          */
49269         'alphaMask' : /[a-z_]/i,
49270
49271         /**
49272          * The function used to validate alphanumeric values
49273          * @param {String} value The value
49274          */
49275         'alphanum' : function(v){
49276             return alphanum.test(v);
49277         },
49278         /**
49279          * The error text to display when the alphanumeric validation function returns false
49280          * @type String
49281          */
49282         'alphanumText' : 'This field should only contain letters, numbers and _',
49283         /**
49284          * The keystroke filter mask to be applied on alphanumeric input
49285          * @type RegExp
49286          */
49287         'alphanumMask' : /[a-z0-9_]/i
49288     };
49289 }();//<script type="text/javascript">
49290
49291 /**
49292  * @class Roo.form.FCKeditor
49293  * @extends Roo.form.TextArea
49294  * Wrapper around the FCKEditor http://www.fckeditor.net
49295  * @constructor
49296  * Creates a new FCKeditor
49297  * @param {Object} config Configuration options
49298  */
49299 Roo.form.FCKeditor = function(config){
49300     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49301     this.addEvents({
49302          /**
49303          * @event editorinit
49304          * Fired when the editor is initialized - you can add extra handlers here..
49305          * @param {FCKeditor} this
49306          * @param {Object} the FCK object.
49307          */
49308         editorinit : true
49309     });
49310     
49311     
49312 };
49313 Roo.form.FCKeditor.editors = { };
49314 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49315 {
49316     //defaultAutoCreate : {
49317     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49318     //},
49319     // private
49320     /**
49321      * @cfg {Object} fck options - see fck manual for details.
49322      */
49323     fckconfig : false,
49324     
49325     /**
49326      * @cfg {Object} fck toolbar set (Basic or Default)
49327      */
49328     toolbarSet : 'Basic',
49329     /**
49330      * @cfg {Object} fck BasePath
49331      */ 
49332     basePath : '/fckeditor/',
49333     
49334     
49335     frame : false,
49336     
49337     value : '',
49338     
49339    
49340     onRender : function(ct, position)
49341     {
49342         if(!this.el){
49343             this.defaultAutoCreate = {
49344                 tag: "textarea",
49345                 style:"width:300px;height:60px;",
49346                 autocomplete: "new-password"
49347             };
49348         }
49349         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49350         /*
49351         if(this.grow){
49352             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49353             if(this.preventScrollbars){
49354                 this.el.setStyle("overflow", "hidden");
49355             }
49356             this.el.setHeight(this.growMin);
49357         }
49358         */
49359         //console.log('onrender' + this.getId() );
49360         Roo.form.FCKeditor.editors[this.getId()] = this;
49361          
49362
49363         this.replaceTextarea() ;
49364         
49365     },
49366     
49367     getEditor : function() {
49368         return this.fckEditor;
49369     },
49370     /**
49371      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49372      * @param {Mixed} value The value to set
49373      */
49374     
49375     
49376     setValue : function(value)
49377     {
49378         //console.log('setValue: ' + value);
49379         
49380         if(typeof(value) == 'undefined') { // not sure why this is happending...
49381             return;
49382         }
49383         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49384         
49385         //if(!this.el || !this.getEditor()) {
49386         //    this.value = value;
49387             //this.setValue.defer(100,this,[value]);    
49388         //    return;
49389         //} 
49390         
49391         if(!this.getEditor()) {
49392             return;
49393         }
49394         
49395         this.getEditor().SetData(value);
49396         
49397         //
49398
49399     },
49400
49401     /**
49402      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49403      * @return {Mixed} value The field value
49404      */
49405     getValue : function()
49406     {
49407         
49408         if (this.frame && this.frame.dom.style.display == 'none') {
49409             return Roo.form.FCKeditor.superclass.getValue.call(this);
49410         }
49411         
49412         if(!this.el || !this.getEditor()) {
49413            
49414            // this.getValue.defer(100,this); 
49415             return this.value;
49416         }
49417        
49418         
49419         var value=this.getEditor().GetData();
49420         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49421         return Roo.form.FCKeditor.superclass.getValue.call(this);
49422         
49423
49424     },
49425
49426     /**
49427      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49428      * @return {Mixed} value The field value
49429      */
49430     getRawValue : function()
49431     {
49432         if (this.frame && this.frame.dom.style.display == 'none') {
49433             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49434         }
49435         
49436         if(!this.el || !this.getEditor()) {
49437             //this.getRawValue.defer(100,this); 
49438             return this.value;
49439             return;
49440         }
49441         
49442         
49443         
49444         var value=this.getEditor().GetData();
49445         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49446         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49447          
49448     },
49449     
49450     setSize : function(w,h) {
49451         
49452         
49453         
49454         //if (this.frame && this.frame.dom.style.display == 'none') {
49455         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49456         //    return;
49457         //}
49458         //if(!this.el || !this.getEditor()) {
49459         //    this.setSize.defer(100,this, [w,h]); 
49460         //    return;
49461         //}
49462         
49463         
49464         
49465         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49466         
49467         this.frame.dom.setAttribute('width', w);
49468         this.frame.dom.setAttribute('height', h);
49469         this.frame.setSize(w,h);
49470         
49471     },
49472     
49473     toggleSourceEdit : function(value) {
49474         
49475       
49476          
49477         this.el.dom.style.display = value ? '' : 'none';
49478         this.frame.dom.style.display = value ?  'none' : '';
49479         
49480     },
49481     
49482     
49483     focus: function(tag)
49484     {
49485         if (this.frame.dom.style.display == 'none') {
49486             return Roo.form.FCKeditor.superclass.focus.call(this);
49487         }
49488         if(!this.el || !this.getEditor()) {
49489             this.focus.defer(100,this, [tag]); 
49490             return;
49491         }
49492         
49493         
49494         
49495         
49496         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49497         this.getEditor().Focus();
49498         if (tgs.length) {
49499             if (!this.getEditor().Selection.GetSelection()) {
49500                 this.focus.defer(100,this, [tag]); 
49501                 return;
49502             }
49503             
49504             
49505             var r = this.getEditor().EditorDocument.createRange();
49506             r.setStart(tgs[0],0);
49507             r.setEnd(tgs[0],0);
49508             this.getEditor().Selection.GetSelection().removeAllRanges();
49509             this.getEditor().Selection.GetSelection().addRange(r);
49510             this.getEditor().Focus();
49511         }
49512         
49513     },
49514     
49515     
49516     
49517     replaceTextarea : function()
49518     {
49519         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49520             return ;
49521         }
49522         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49523         //{
49524             // We must check the elements firstly using the Id and then the name.
49525         var oTextarea = document.getElementById( this.getId() );
49526         
49527         var colElementsByName = document.getElementsByName( this.getId() ) ;
49528          
49529         oTextarea.style.display = 'none' ;
49530
49531         if ( oTextarea.tabIndex ) {            
49532             this.TabIndex = oTextarea.tabIndex ;
49533         }
49534         
49535         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49536         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49537         this.frame = Roo.get(this.getId() + '___Frame')
49538     },
49539     
49540     _getConfigHtml : function()
49541     {
49542         var sConfig = '' ;
49543
49544         for ( var o in this.fckconfig ) {
49545             sConfig += sConfig.length > 0  ? '&amp;' : '';
49546             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49547         }
49548
49549         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49550     },
49551     
49552     
49553     _getIFrameHtml : function()
49554     {
49555         var sFile = 'fckeditor.html' ;
49556         /* no idea what this is about..
49557         try
49558         {
49559             if ( (/fcksource=true/i).test( window.top.location.search ) )
49560                 sFile = 'fckeditor.original.html' ;
49561         }
49562         catch (e) { 
49563         */
49564
49565         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49566         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49567         
49568         
49569         var html = '<iframe id="' + this.getId() +
49570             '___Frame" src="' + sLink +
49571             '" width="' + this.width +
49572             '" height="' + this.height + '"' +
49573             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49574             ' frameborder="0" scrolling="no"></iframe>' ;
49575
49576         return html ;
49577     },
49578     
49579     _insertHtmlBefore : function( html, element )
49580     {
49581         if ( element.insertAdjacentHTML )       {
49582             // IE
49583             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49584         } else { // Gecko
49585             var oRange = document.createRange() ;
49586             oRange.setStartBefore( element ) ;
49587             var oFragment = oRange.createContextualFragment( html );
49588             element.parentNode.insertBefore( oFragment, element ) ;
49589         }
49590     }
49591     
49592     
49593   
49594     
49595     
49596     
49597     
49598
49599 });
49600
49601 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49602
49603 function FCKeditor_OnComplete(editorInstance){
49604     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49605     f.fckEditor = editorInstance;
49606     //console.log("loaded");
49607     f.fireEvent('editorinit', f, editorInstance);
49608
49609   
49610
49611  
49612
49613
49614
49615
49616
49617
49618
49619
49620
49621
49622
49623
49624
49625
49626
49627 //<script type="text/javascript">
49628 /**
49629  * @class Roo.form.GridField
49630  * @extends Roo.form.Field
49631  * Embed a grid (or editable grid into a form)
49632  * STATUS ALPHA
49633  * 
49634  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49635  * it needs 
49636  * xgrid.store = Roo.data.Store
49637  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49638  * xgrid.store.reader = Roo.data.JsonReader 
49639  * 
49640  * 
49641  * @constructor
49642  * Creates a new GridField
49643  * @param {Object} config Configuration options
49644  */
49645 Roo.form.GridField = function(config){
49646     Roo.form.GridField.superclass.constructor.call(this, config);
49647      
49648 };
49649
49650 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49651     /**
49652      * @cfg {Number} width  - used to restrict width of grid..
49653      */
49654     width : 100,
49655     /**
49656      * @cfg {Number} height - used to restrict height of grid..
49657      */
49658     height : 50,
49659      /**
49660      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49661          * 
49662          *}
49663      */
49664     xgrid : false, 
49665     /**
49666      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49667      * {tag: "input", type: "checkbox", autocomplete: "off"})
49668      */
49669    // defaultAutoCreate : { tag: 'div' },
49670     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49671     /**
49672      * @cfg {String} addTitle Text to include for adding a title.
49673      */
49674     addTitle : false,
49675     //
49676     onResize : function(){
49677         Roo.form.Field.superclass.onResize.apply(this, arguments);
49678     },
49679
49680     initEvents : function(){
49681         // Roo.form.Checkbox.superclass.initEvents.call(this);
49682         // has no events...
49683        
49684     },
49685
49686
49687     getResizeEl : function(){
49688         return this.wrap;
49689     },
49690
49691     getPositionEl : function(){
49692         return this.wrap;
49693     },
49694
49695     // private
49696     onRender : function(ct, position){
49697         
49698         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49699         var style = this.style;
49700         delete this.style;
49701         
49702         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49703         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49704         this.viewEl = this.wrap.createChild({ tag: 'div' });
49705         if (style) {
49706             this.viewEl.applyStyles(style);
49707         }
49708         if (this.width) {
49709             this.viewEl.setWidth(this.width);
49710         }
49711         if (this.height) {
49712             this.viewEl.setHeight(this.height);
49713         }
49714         //if(this.inputValue !== undefined){
49715         //this.setValue(this.value);
49716         
49717         
49718         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49719         
49720         
49721         this.grid.render();
49722         this.grid.getDataSource().on('remove', this.refreshValue, this);
49723         this.grid.getDataSource().on('update', this.refreshValue, this);
49724         this.grid.on('afteredit', this.refreshValue, this);
49725  
49726     },
49727      
49728     
49729     /**
49730      * Sets the value of the item. 
49731      * @param {String} either an object  or a string..
49732      */
49733     setValue : function(v){
49734         //this.value = v;
49735         v = v || []; // empty set..
49736         // this does not seem smart - it really only affects memoryproxy grids..
49737         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49738             var ds = this.grid.getDataSource();
49739             // assumes a json reader..
49740             var data = {}
49741             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49742             ds.loadData( data);
49743         }
49744         // clear selection so it does not get stale.
49745         if (this.grid.sm) { 
49746             this.grid.sm.clearSelections();
49747         }
49748         
49749         Roo.form.GridField.superclass.setValue.call(this, v);
49750         this.refreshValue();
49751         // should load data in the grid really....
49752     },
49753     
49754     // private
49755     refreshValue: function() {
49756          var val = [];
49757         this.grid.getDataSource().each(function(r) {
49758             val.push(r.data);
49759         });
49760         this.el.dom.value = Roo.encode(val);
49761     }
49762     
49763      
49764     
49765     
49766 });/*
49767  * Based on:
49768  * Ext JS Library 1.1.1
49769  * Copyright(c) 2006-2007, Ext JS, LLC.
49770  *
49771  * Originally Released Under LGPL - original licence link has changed is not relivant.
49772  *
49773  * Fork - LGPL
49774  * <script type="text/javascript">
49775  */
49776 /**
49777  * @class Roo.form.DisplayField
49778  * @extends Roo.form.Field
49779  * A generic Field to display non-editable data.
49780  * @cfg {Boolean} closable (true|false) default false
49781  * @constructor
49782  * Creates a new Display Field item.
49783  * @param {Object} config Configuration options
49784  */
49785 Roo.form.DisplayField = function(config){
49786     Roo.form.DisplayField.superclass.constructor.call(this, config);
49787     
49788     this.addEvents({
49789         /**
49790          * @event close
49791          * Fires after the click the close btn
49792              * @param {Roo.form.DisplayField} this
49793              */
49794         close : true
49795     });
49796 };
49797
49798 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49799     inputType:      'hidden',
49800     allowBlank:     true,
49801     readOnly:         true,
49802     
49803  
49804     /**
49805      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49806      */
49807     focusClass : undefined,
49808     /**
49809      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49810      */
49811     fieldClass: 'x-form-field',
49812     
49813      /**
49814      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49815      */
49816     valueRenderer: undefined,
49817     
49818     width: 100,
49819     /**
49820      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49821      * {tag: "input", type: "checkbox", autocomplete: "off"})
49822      */
49823      
49824  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49825  
49826     closable : false,
49827     
49828     onResize : function(){
49829         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49830         
49831     },
49832
49833     initEvents : function(){
49834         // Roo.form.Checkbox.superclass.initEvents.call(this);
49835         // has no events...
49836         
49837         if(this.closable){
49838             this.closeEl.on('click', this.onClose, this);
49839         }
49840        
49841     },
49842
49843
49844     getResizeEl : function(){
49845         return this.wrap;
49846     },
49847
49848     getPositionEl : function(){
49849         return this.wrap;
49850     },
49851
49852     // private
49853     onRender : function(ct, position){
49854         
49855         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49856         //if(this.inputValue !== undefined){
49857         this.wrap = this.el.wrap();
49858         
49859         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49860         
49861         if(this.closable){
49862             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49863         }
49864         
49865         if (this.bodyStyle) {
49866             this.viewEl.applyStyles(this.bodyStyle);
49867         }
49868         //this.viewEl.setStyle('padding', '2px');
49869         
49870         this.setValue(this.value);
49871         
49872     },
49873 /*
49874     // private
49875     initValue : Roo.emptyFn,
49876
49877   */
49878
49879         // private
49880     onClick : function(){
49881         
49882     },
49883
49884     /**
49885      * Sets the checked state of the checkbox.
49886      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49887      */
49888     setValue : function(v){
49889         this.value = v;
49890         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49891         // this might be called before we have a dom element..
49892         if (!this.viewEl) {
49893             return;
49894         }
49895         this.viewEl.dom.innerHTML = html;
49896         Roo.form.DisplayField.superclass.setValue.call(this, v);
49897
49898     },
49899     
49900     onClose : function(e)
49901     {
49902         e.preventDefault();
49903         
49904         this.fireEvent('close', this);
49905     }
49906 });/*
49907  * 
49908  * Licence- LGPL
49909  * 
49910  */
49911
49912 /**
49913  * @class Roo.form.DayPicker
49914  * @extends Roo.form.Field
49915  * A Day picker show [M] [T] [W] ....
49916  * @constructor
49917  * Creates a new Day Picker
49918  * @param {Object} config Configuration options
49919  */
49920 Roo.form.DayPicker= function(config){
49921     Roo.form.DayPicker.superclass.constructor.call(this, config);
49922      
49923 };
49924
49925 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49926     /**
49927      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49928      */
49929     focusClass : undefined,
49930     /**
49931      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49932      */
49933     fieldClass: "x-form-field",
49934    
49935     /**
49936      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49937      * {tag: "input", type: "checkbox", autocomplete: "off"})
49938      */
49939     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49940     
49941    
49942     actionMode : 'viewEl', 
49943     //
49944     // private
49945  
49946     inputType : 'hidden',
49947     
49948      
49949     inputElement: false, // real input element?
49950     basedOn: false, // ????
49951     
49952     isFormField: true, // not sure where this is needed!!!!
49953
49954     onResize : function(){
49955         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49956         if(!this.boxLabel){
49957             this.el.alignTo(this.wrap, 'c-c');
49958         }
49959     },
49960
49961     initEvents : function(){
49962         Roo.form.Checkbox.superclass.initEvents.call(this);
49963         this.el.on("click", this.onClick,  this);
49964         this.el.on("change", this.onClick,  this);
49965     },
49966
49967
49968     getResizeEl : function(){
49969         return this.wrap;
49970     },
49971
49972     getPositionEl : function(){
49973         return this.wrap;
49974     },
49975
49976     
49977     // private
49978     onRender : function(ct, position){
49979         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49980        
49981         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49982         
49983         var r1 = '<table><tr>';
49984         var r2 = '<tr class="x-form-daypick-icons">';
49985         for (var i=0; i < 7; i++) {
49986             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49987             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49988         }
49989         
49990         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49991         viewEl.select('img').on('click', this.onClick, this);
49992         this.viewEl = viewEl;   
49993         
49994         
49995         // this will not work on Chrome!!!
49996         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49997         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49998         
49999         
50000           
50001
50002     },
50003
50004     // private
50005     initValue : Roo.emptyFn,
50006
50007     /**
50008      * Returns the checked state of the checkbox.
50009      * @return {Boolean} True if checked, else false
50010      */
50011     getValue : function(){
50012         return this.el.dom.value;
50013         
50014     },
50015
50016         // private
50017     onClick : function(e){ 
50018         //this.setChecked(!this.checked);
50019         Roo.get(e.target).toggleClass('x-menu-item-checked');
50020         this.refreshValue();
50021         //if(this.el.dom.checked != this.checked){
50022         //    this.setValue(this.el.dom.checked);
50023        // }
50024     },
50025     
50026     // private
50027     refreshValue : function()
50028     {
50029         var val = '';
50030         this.viewEl.select('img',true).each(function(e,i,n)  {
50031             val += e.is(".x-menu-item-checked") ? String(n) : '';
50032         });
50033         this.setValue(val, true);
50034     },
50035
50036     /**
50037      * Sets the checked state of the checkbox.
50038      * On is always based on a string comparison between inputValue and the param.
50039      * @param {Boolean/String} value - the value to set 
50040      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50041      */
50042     setValue : function(v,suppressEvent){
50043         if (!this.el.dom) {
50044             return;
50045         }
50046         var old = this.el.dom.value ;
50047         this.el.dom.value = v;
50048         if (suppressEvent) {
50049             return ;
50050         }
50051          
50052         // update display..
50053         this.viewEl.select('img',true).each(function(e,i,n)  {
50054             
50055             var on = e.is(".x-menu-item-checked");
50056             var newv = v.indexOf(String(n)) > -1;
50057             if (on != newv) {
50058                 e.toggleClass('x-menu-item-checked');
50059             }
50060             
50061         });
50062         
50063         
50064         this.fireEvent('change', this, v, old);
50065         
50066         
50067     },
50068    
50069     // handle setting of hidden value by some other method!!?!?
50070     setFromHidden: function()
50071     {
50072         if(!this.el){
50073             return;
50074         }
50075         //console.log("SET FROM HIDDEN");
50076         //alert('setFrom hidden');
50077         this.setValue(this.el.dom.value);
50078     },
50079     
50080     onDestroy : function()
50081     {
50082         if(this.viewEl){
50083             Roo.get(this.viewEl).remove();
50084         }
50085          
50086         Roo.form.DayPicker.superclass.onDestroy.call(this);
50087     }
50088
50089 });/*
50090  * RooJS Library 1.1.1
50091  * Copyright(c) 2008-2011  Alan Knowles
50092  *
50093  * License - LGPL
50094  */
50095  
50096
50097 /**
50098  * @class Roo.form.ComboCheck
50099  * @extends Roo.form.ComboBox
50100  * A combobox for multiple select items.
50101  *
50102  * FIXME - could do with a reset button..
50103  * 
50104  * @constructor
50105  * Create a new ComboCheck
50106  * @param {Object} config Configuration options
50107  */
50108 Roo.form.ComboCheck = function(config){
50109     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50110     // should verify some data...
50111     // like
50112     // hiddenName = required..
50113     // displayField = required
50114     // valudField == required
50115     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50116     var _t = this;
50117     Roo.each(req, function(e) {
50118         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50119             throw "Roo.form.ComboCheck : missing value for: " + e;
50120         }
50121     });
50122     
50123     
50124 };
50125
50126 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50127      
50128      
50129     editable : false,
50130      
50131     selectedClass: 'x-menu-item-checked', 
50132     
50133     // private
50134     onRender : function(ct, position){
50135         var _t = this;
50136         
50137         
50138         
50139         if(!this.tpl){
50140             var cls = 'x-combo-list';
50141
50142             
50143             this.tpl =  new Roo.Template({
50144                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50145                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50146                    '<span>{' + this.displayField + '}</span>' +
50147                     '</div>' 
50148                 
50149             });
50150         }
50151  
50152         
50153         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50154         this.view.singleSelect = false;
50155         this.view.multiSelect = true;
50156         this.view.toggleSelect = true;
50157         this.pageTb.add(new Roo.Toolbar.Fill(), {
50158             
50159             text: 'Done',
50160             handler: function()
50161             {
50162                 _t.collapse();
50163             }
50164         });
50165     },
50166     
50167     onViewOver : function(e, t){
50168         // do nothing...
50169         return;
50170         
50171     },
50172     
50173     onViewClick : function(doFocus,index){
50174         return;
50175         
50176     },
50177     select: function () {
50178         //Roo.log("SELECT CALLED");
50179     },
50180      
50181     selectByValue : function(xv, scrollIntoView){
50182         var ar = this.getValueArray();
50183         var sels = [];
50184         
50185         Roo.each(ar, function(v) {
50186             if(v === undefined || v === null){
50187                 return;
50188             }
50189             var r = this.findRecord(this.valueField, v);
50190             if(r){
50191                 sels.push(this.store.indexOf(r))
50192                 
50193             }
50194         },this);
50195         this.view.select(sels);
50196         return false;
50197     },
50198     
50199     
50200     
50201     onSelect : function(record, index){
50202        // Roo.log("onselect Called");
50203        // this is only called by the clear button now..
50204         this.view.clearSelections();
50205         this.setValue('[]');
50206         if (this.value != this.valueBefore) {
50207             this.fireEvent('change', this, this.value, this.valueBefore);
50208             this.valueBefore = this.value;
50209         }
50210     },
50211     getValueArray : function()
50212     {
50213         var ar = [] ;
50214         
50215         try {
50216             //Roo.log(this.value);
50217             if (typeof(this.value) == 'undefined') {
50218                 return [];
50219             }
50220             var ar = Roo.decode(this.value);
50221             return  ar instanceof Array ? ar : []; //?? valid?
50222             
50223         } catch(e) {
50224             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50225             return [];
50226         }
50227          
50228     },
50229     expand : function ()
50230     {
50231         
50232         Roo.form.ComboCheck.superclass.expand.call(this);
50233         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50234         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50235         
50236
50237     },
50238     
50239     collapse : function(){
50240         Roo.form.ComboCheck.superclass.collapse.call(this);
50241         var sl = this.view.getSelectedIndexes();
50242         var st = this.store;
50243         var nv = [];
50244         var tv = [];
50245         var r;
50246         Roo.each(sl, function(i) {
50247             r = st.getAt(i);
50248             nv.push(r.get(this.valueField));
50249         },this);
50250         this.setValue(Roo.encode(nv));
50251         if (this.value != this.valueBefore) {
50252
50253             this.fireEvent('change', this, this.value, this.valueBefore);
50254             this.valueBefore = this.value;
50255         }
50256         
50257     },
50258     
50259     setValue : function(v){
50260         // Roo.log(v);
50261         this.value = v;
50262         
50263         var vals = this.getValueArray();
50264         var tv = [];
50265         Roo.each(vals, function(k) {
50266             var r = this.findRecord(this.valueField, k);
50267             if(r){
50268                 tv.push(r.data[this.displayField]);
50269             }else if(this.valueNotFoundText !== undefined){
50270                 tv.push( this.valueNotFoundText );
50271             }
50272         },this);
50273        // Roo.log(tv);
50274         
50275         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50276         this.hiddenField.value = v;
50277         this.value = v;
50278     }
50279     
50280 });/*
50281  * Based on:
50282  * Ext JS Library 1.1.1
50283  * Copyright(c) 2006-2007, Ext JS, LLC.
50284  *
50285  * Originally Released Under LGPL - original licence link has changed is not relivant.
50286  *
50287  * Fork - LGPL
50288  * <script type="text/javascript">
50289  */
50290  
50291 /**
50292  * @class Roo.form.Signature
50293  * @extends Roo.form.Field
50294  * Signature field.  
50295  * @constructor
50296  * 
50297  * @param {Object} config Configuration options
50298  */
50299
50300 Roo.form.Signature = function(config){
50301     Roo.form.Signature.superclass.constructor.call(this, config);
50302     
50303     this.addEvents({// not in used??
50304          /**
50305          * @event confirm
50306          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50307              * @param {Roo.form.Signature} combo This combo box
50308              */
50309         'confirm' : true,
50310         /**
50311          * @event reset
50312          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50313              * @param {Roo.form.ComboBox} combo This combo box
50314              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50315              */
50316         'reset' : true
50317     });
50318 };
50319
50320 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50321     /**
50322      * @cfg {Object} labels Label to use when rendering a form.
50323      * defaults to 
50324      * labels : { 
50325      *      clear : "Clear",
50326      *      confirm : "Confirm"
50327      *  }
50328      */
50329     labels : { 
50330         clear : "Clear",
50331         confirm : "Confirm"
50332     },
50333     /**
50334      * @cfg {Number} width The signature panel width (defaults to 300)
50335      */
50336     width: 300,
50337     /**
50338      * @cfg {Number} height The signature panel height (defaults to 100)
50339      */
50340     height : 100,
50341     /**
50342      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50343      */
50344     allowBlank : false,
50345     
50346     //private
50347     // {Object} signPanel The signature SVG panel element (defaults to {})
50348     signPanel : {},
50349     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50350     isMouseDown : false,
50351     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50352     isConfirmed : false,
50353     // {String} signatureTmp SVG mapping string (defaults to empty string)
50354     signatureTmp : '',
50355     
50356     
50357     defaultAutoCreate : { // modified by initCompnoent..
50358         tag: "input",
50359         type:"hidden"
50360     },
50361
50362     // private
50363     onRender : function(ct, position){
50364         
50365         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50366         
50367         this.wrap = this.el.wrap({
50368             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50369         });
50370         
50371         this.createToolbar(this);
50372         this.signPanel = this.wrap.createChild({
50373                 tag: 'div',
50374                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50375             }, this.el
50376         );
50377             
50378         this.svgID = Roo.id();
50379         this.svgEl = this.signPanel.createChild({
50380               xmlns : 'http://www.w3.org/2000/svg',
50381               tag : 'svg',
50382               id : this.svgID + "-svg",
50383               width: this.width,
50384               height: this.height,
50385               viewBox: '0 0 '+this.width+' '+this.height,
50386               cn : [
50387                 {
50388                     tag: "rect",
50389                     id: this.svgID + "-svg-r",
50390                     width: this.width,
50391                     height: this.height,
50392                     fill: "#ffa"
50393                 },
50394                 {
50395                     tag: "line",
50396                     id: this.svgID + "-svg-l",
50397                     x1: "0", // start
50398                     y1: (this.height*0.8), // start set the line in 80% of height
50399                     x2: this.width, // end
50400                     y2: (this.height*0.8), // end set the line in 80% of height
50401                     'stroke': "#666",
50402                     'stroke-width': "1",
50403                     'stroke-dasharray': "3",
50404                     'shape-rendering': "crispEdges",
50405                     'pointer-events': "none"
50406                 },
50407                 {
50408                     tag: "path",
50409                     id: this.svgID + "-svg-p",
50410                     'stroke': "navy",
50411                     'stroke-width': "3",
50412                     'fill': "none",
50413                     'pointer-events': 'none'
50414                 }
50415               ]
50416         });
50417         this.createSVG();
50418         this.svgBox = this.svgEl.dom.getScreenCTM();
50419     },
50420     createSVG : function(){ 
50421         var svg = this.signPanel;
50422         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50423         var t = this;
50424
50425         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50426         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50427         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50428         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50429         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50430         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50431         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50432         
50433     },
50434     isTouchEvent : function(e){
50435         return e.type.match(/^touch/);
50436     },
50437     getCoords : function (e) {
50438         var pt    = this.svgEl.dom.createSVGPoint();
50439         pt.x = e.clientX; 
50440         pt.y = e.clientY;
50441         if (this.isTouchEvent(e)) {
50442             pt.x =  e.targetTouches[0].clientX;
50443             pt.y = e.targetTouches[0].clientY;
50444         }
50445         var a = this.svgEl.dom.getScreenCTM();
50446         var b = a.inverse();
50447         var mx = pt.matrixTransform(b);
50448         return mx.x + ',' + mx.y;
50449     },
50450     //mouse event headler 
50451     down : function (e) {
50452         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50453         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50454         
50455         this.isMouseDown = true;
50456         
50457         e.preventDefault();
50458     },
50459     move : function (e) {
50460         if (this.isMouseDown) {
50461             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50462             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50463         }
50464         
50465         e.preventDefault();
50466     },
50467     up : function (e) {
50468         this.isMouseDown = false;
50469         var sp = this.signatureTmp.split(' ');
50470         
50471         if(sp.length > 1){
50472             if(!sp[sp.length-2].match(/^L/)){
50473                 sp.pop();
50474                 sp.pop();
50475                 sp.push("");
50476                 this.signatureTmp = sp.join(" ");
50477             }
50478         }
50479         if(this.getValue() != this.signatureTmp){
50480             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50481             this.isConfirmed = false;
50482         }
50483         e.preventDefault();
50484     },
50485     
50486     /**
50487      * Protected method that will not generally be called directly. It
50488      * is called when the editor creates its toolbar. Override this method if you need to
50489      * add custom toolbar buttons.
50490      * @param {HtmlEditor} editor
50491      */
50492     createToolbar : function(editor){
50493          function btn(id, toggle, handler){
50494             var xid = fid + '-'+ id ;
50495             return {
50496                 id : xid,
50497                 cmd : id,
50498                 cls : 'x-btn-icon x-edit-'+id,
50499                 enableToggle:toggle !== false,
50500                 scope: editor, // was editor...
50501                 handler:handler||editor.relayBtnCmd,
50502                 clickEvent:'mousedown',
50503                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50504                 tabIndex:-1
50505             };
50506         }
50507         
50508         
50509         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50510         this.tb = tb;
50511         this.tb.add(
50512            {
50513                 cls : ' x-signature-btn x-signature-'+id,
50514                 scope: editor, // was editor...
50515                 handler: this.reset,
50516                 clickEvent:'mousedown',
50517                 text: this.labels.clear
50518             },
50519             {
50520                  xtype : 'Fill',
50521                  xns: Roo.Toolbar
50522             }, 
50523             {
50524                 cls : '  x-signature-btn x-signature-'+id,
50525                 scope: editor, // was editor...
50526                 handler: this.confirmHandler,
50527                 clickEvent:'mousedown',
50528                 text: this.labels.confirm
50529             }
50530         );
50531     
50532     },
50533     //public
50534     /**
50535      * when user is clicked confirm then show this image.....
50536      * 
50537      * @return {String} Image Data URI
50538      */
50539     getImageDataURI : function(){
50540         var svg = this.svgEl.dom.parentNode.innerHTML;
50541         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50542         return src; 
50543     },
50544     /**
50545      * 
50546      * @return {Boolean} this.isConfirmed
50547      */
50548     getConfirmed : function(){
50549         return this.isConfirmed;
50550     },
50551     /**
50552      * 
50553      * @return {Number} this.width
50554      */
50555     getWidth : function(){
50556         return this.width;
50557     },
50558     /**
50559      * 
50560      * @return {Number} this.height
50561      */
50562     getHeight : function(){
50563         return this.height;
50564     },
50565     // private
50566     getSignature : function(){
50567         return this.signatureTmp;
50568     },
50569     // private
50570     reset : function(){
50571         this.signatureTmp = '';
50572         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50573         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50574         this.isConfirmed = false;
50575         Roo.form.Signature.superclass.reset.call(this);
50576     },
50577     setSignature : function(s){
50578         this.signatureTmp = s;
50579         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50580         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50581         this.setValue(s);
50582         this.isConfirmed = false;
50583         Roo.form.Signature.superclass.reset.call(this);
50584     }, 
50585     test : function(){
50586 //        Roo.log(this.signPanel.dom.contentWindow.up())
50587     },
50588     //private
50589     setConfirmed : function(){
50590         
50591         
50592         
50593 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50594     },
50595     // private
50596     confirmHandler : function(){
50597         if(!this.getSignature()){
50598             return;
50599         }
50600         
50601         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50602         this.setValue(this.getSignature());
50603         this.isConfirmed = true;
50604         
50605         this.fireEvent('confirm', this);
50606     },
50607     // private
50608     // Subclasses should provide the validation implementation by overriding this
50609     validateValue : function(value){
50610         if(this.allowBlank){
50611             return true;
50612         }
50613         
50614         if(this.isConfirmed){
50615             return true;
50616         }
50617         return false;
50618     }
50619 });/*
50620  * Based on:
50621  * Ext JS Library 1.1.1
50622  * Copyright(c) 2006-2007, Ext JS, LLC.
50623  *
50624  * Originally Released Under LGPL - original licence link has changed is not relivant.
50625  *
50626  * Fork - LGPL
50627  * <script type="text/javascript">
50628  */
50629  
50630
50631 /**
50632  * @class Roo.form.ComboBox
50633  * @extends Roo.form.TriggerField
50634  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50635  * @constructor
50636  * Create a new ComboBox.
50637  * @param {Object} config Configuration options
50638  */
50639 Roo.form.Select = function(config){
50640     Roo.form.Select.superclass.constructor.call(this, config);
50641      
50642 };
50643
50644 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50645     /**
50646      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50647      */
50648     /**
50649      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50650      * rendering into an Roo.Editor, defaults to false)
50651      */
50652     /**
50653      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50654      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50655      */
50656     /**
50657      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50658      */
50659     /**
50660      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50661      * the dropdown list (defaults to undefined, with no header element)
50662      */
50663
50664      /**
50665      * @cfg {String/Roo.Template} tpl The template to use to render the output
50666      */
50667      
50668     // private
50669     defaultAutoCreate : {tag: "select"  },
50670     /**
50671      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50672      */
50673     listWidth: undefined,
50674     /**
50675      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50676      * mode = 'remote' or 'text' if mode = 'local')
50677      */
50678     displayField: undefined,
50679     /**
50680      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50681      * mode = 'remote' or 'value' if mode = 'local'). 
50682      * Note: use of a valueField requires the user make a selection
50683      * in order for a value to be mapped.
50684      */
50685     valueField: undefined,
50686     
50687     
50688     /**
50689      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50690      * field's data value (defaults to the underlying DOM element's name)
50691      */
50692     hiddenName: undefined,
50693     /**
50694      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50695      */
50696     listClass: '',
50697     /**
50698      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50699      */
50700     selectedClass: 'x-combo-selected',
50701     /**
50702      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50703      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50704      * which displays a downward arrow icon).
50705      */
50706     triggerClass : 'x-form-arrow-trigger',
50707     /**
50708      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50709      */
50710     shadow:'sides',
50711     /**
50712      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50713      * anchor positions (defaults to 'tl-bl')
50714      */
50715     listAlign: 'tl-bl?',
50716     /**
50717      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50718      */
50719     maxHeight: 300,
50720     /**
50721      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50722      * query specified by the allQuery config option (defaults to 'query')
50723      */
50724     triggerAction: 'query',
50725     /**
50726      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50727      * (defaults to 4, does not apply if editable = false)
50728      */
50729     minChars : 4,
50730     /**
50731      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50732      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50733      */
50734     typeAhead: false,
50735     /**
50736      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50737      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50738      */
50739     queryDelay: 500,
50740     /**
50741      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50742      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50743      */
50744     pageSize: 0,
50745     /**
50746      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50747      * when editable = true (defaults to false)
50748      */
50749     selectOnFocus:false,
50750     /**
50751      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50752      */
50753     queryParam: 'query',
50754     /**
50755      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50756      * when mode = 'remote' (defaults to 'Loading...')
50757      */
50758     loadingText: 'Loading...',
50759     /**
50760      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50761      */
50762     resizable: false,
50763     /**
50764      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50765      */
50766     handleHeight : 8,
50767     /**
50768      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50769      * traditional select (defaults to true)
50770      */
50771     editable: true,
50772     /**
50773      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50774      */
50775     allQuery: '',
50776     /**
50777      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50778      */
50779     mode: 'remote',
50780     /**
50781      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50782      * listWidth has a higher value)
50783      */
50784     minListWidth : 70,
50785     /**
50786      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50787      * allow the user to set arbitrary text into the field (defaults to false)
50788      */
50789     forceSelection:false,
50790     /**
50791      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50792      * if typeAhead = true (defaults to 250)
50793      */
50794     typeAheadDelay : 250,
50795     /**
50796      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50797      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50798      */
50799     valueNotFoundText : undefined,
50800     
50801     /**
50802      * @cfg {String} defaultValue The value displayed after loading the store.
50803      */
50804     defaultValue: '',
50805     
50806     /**
50807      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50808      */
50809     blockFocus : false,
50810     
50811     /**
50812      * @cfg {Boolean} disableClear Disable showing of clear button.
50813      */
50814     disableClear : false,
50815     /**
50816      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50817      */
50818     alwaysQuery : false,
50819     
50820     //private
50821     addicon : false,
50822     editicon: false,
50823     
50824     // element that contains real text value.. (when hidden is used..)
50825      
50826     // private
50827     onRender : function(ct, position){
50828         Roo.form.Field.prototype.onRender.call(this, ct, position);
50829         
50830         if(this.store){
50831             this.store.on('beforeload', this.onBeforeLoad, this);
50832             this.store.on('load', this.onLoad, this);
50833             this.store.on('loadexception', this.onLoadException, this);
50834             this.store.load({});
50835         }
50836         
50837         
50838         
50839     },
50840
50841     // private
50842     initEvents : function(){
50843         //Roo.form.ComboBox.superclass.initEvents.call(this);
50844  
50845     },
50846
50847     onDestroy : function(){
50848        
50849         if(this.store){
50850             this.store.un('beforeload', this.onBeforeLoad, this);
50851             this.store.un('load', this.onLoad, this);
50852             this.store.un('loadexception', this.onLoadException, this);
50853         }
50854         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50855     },
50856
50857     // private
50858     fireKey : function(e){
50859         if(e.isNavKeyPress() && !this.list.isVisible()){
50860             this.fireEvent("specialkey", this, e);
50861         }
50862     },
50863
50864     // private
50865     onResize: function(w, h){
50866         
50867         return; 
50868     
50869         
50870     },
50871
50872     /**
50873      * Allow or prevent the user from directly editing the field text.  If false is passed,
50874      * the user will only be able to select from the items defined in the dropdown list.  This method
50875      * is the runtime equivalent of setting the 'editable' config option at config time.
50876      * @param {Boolean} value True to allow the user to directly edit the field text
50877      */
50878     setEditable : function(value){
50879          
50880     },
50881
50882     // private
50883     onBeforeLoad : function(){
50884         
50885         Roo.log("Select before load");
50886         return;
50887     
50888         this.innerList.update(this.loadingText ?
50889                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50890         //this.restrictHeight();
50891         this.selectedIndex = -1;
50892     },
50893
50894     // private
50895     onLoad : function(){
50896
50897     
50898         var dom = this.el.dom;
50899         dom.innerHTML = '';
50900          var od = dom.ownerDocument;
50901          
50902         if (this.emptyText) {
50903             var op = od.createElement('option');
50904             op.setAttribute('value', '');
50905             op.innerHTML = String.format('{0}', this.emptyText);
50906             dom.appendChild(op);
50907         }
50908         if(this.store.getCount() > 0){
50909            
50910             var vf = this.valueField;
50911             var df = this.displayField;
50912             this.store.data.each(function(r) {
50913                 // which colmsn to use... testing - cdoe / title..
50914                 var op = od.createElement('option');
50915                 op.setAttribute('value', r.data[vf]);
50916                 op.innerHTML = String.format('{0}', r.data[df]);
50917                 dom.appendChild(op);
50918             });
50919             if (typeof(this.defaultValue != 'undefined')) {
50920                 this.setValue(this.defaultValue);
50921             }
50922             
50923              
50924         }else{
50925             //this.onEmptyResults();
50926         }
50927         //this.el.focus();
50928     },
50929     // private
50930     onLoadException : function()
50931     {
50932         dom.innerHTML = '';
50933             
50934         Roo.log("Select on load exception");
50935         return;
50936     
50937         this.collapse();
50938         Roo.log(this.store.reader.jsonData);
50939         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50940             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50941         }
50942         
50943         
50944     },
50945     // private
50946     onTypeAhead : function(){
50947          
50948     },
50949
50950     // private
50951     onSelect : function(record, index){
50952         Roo.log('on select?');
50953         return;
50954         if(this.fireEvent('beforeselect', this, record, index) !== false){
50955             this.setFromData(index > -1 ? record.data : false);
50956             this.collapse();
50957             this.fireEvent('select', this, record, index);
50958         }
50959     },
50960
50961     /**
50962      * Returns the currently selected field value or empty string if no value is set.
50963      * @return {String} value The selected value
50964      */
50965     getValue : function(){
50966         var dom = this.el.dom;
50967         this.value = dom.options[dom.selectedIndex].value;
50968         return this.value;
50969         
50970     },
50971
50972     /**
50973      * Clears any text/value currently set in the field
50974      */
50975     clearValue : function(){
50976         this.value = '';
50977         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50978         
50979     },
50980
50981     /**
50982      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50983      * will be displayed in the field.  If the value does not match the data value of an existing item,
50984      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50985      * Otherwise the field will be blank (although the value will still be set).
50986      * @param {String} value The value to match
50987      */
50988     setValue : function(v){
50989         var d = this.el.dom;
50990         for (var i =0; i < d.options.length;i++) {
50991             if (v == d.options[i].value) {
50992                 d.selectedIndex = i;
50993                 this.value = v;
50994                 return;
50995             }
50996         }
50997         this.clearValue();
50998     },
50999     /**
51000      * @property {Object} the last set data for the element
51001      */
51002     
51003     lastData : false,
51004     /**
51005      * Sets the value of the field based on a object which is related to the record format for the store.
51006      * @param {Object} value the value to set as. or false on reset?
51007      */
51008     setFromData : function(o){
51009         Roo.log('setfrom data?');
51010          
51011         
51012         
51013     },
51014     // private
51015     reset : function(){
51016         this.clearValue();
51017     },
51018     // private
51019     findRecord : function(prop, value){
51020         
51021         return false;
51022     
51023         var record;
51024         if(this.store.getCount() > 0){
51025             this.store.each(function(r){
51026                 if(r.data[prop] == value){
51027                     record = r;
51028                     return false;
51029                 }
51030                 return true;
51031             });
51032         }
51033         return record;
51034     },
51035     
51036     getName: function()
51037     {
51038         // returns hidden if it's set..
51039         if (!this.rendered) {return ''};
51040         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51041         
51042     },
51043      
51044
51045     
51046
51047     // private
51048     onEmptyResults : function(){
51049         Roo.log('empty results');
51050         //this.collapse();
51051     },
51052
51053     /**
51054      * Returns true if the dropdown list is expanded, else false.
51055      */
51056     isExpanded : function(){
51057         return false;
51058     },
51059
51060     /**
51061      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51062      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51063      * @param {String} value The data value of the item to select
51064      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51065      * selected item if it is not currently in view (defaults to true)
51066      * @return {Boolean} True if the value matched an item in the list, else false
51067      */
51068     selectByValue : function(v, scrollIntoView){
51069         Roo.log('select By Value');
51070         return false;
51071     
51072         if(v !== undefined && v !== null){
51073             var r = this.findRecord(this.valueField || this.displayField, v);
51074             if(r){
51075                 this.select(this.store.indexOf(r), scrollIntoView);
51076                 return true;
51077             }
51078         }
51079         return false;
51080     },
51081
51082     /**
51083      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51084      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51085      * @param {Number} index The zero-based index of the list item to select
51086      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51087      * selected item if it is not currently in view (defaults to true)
51088      */
51089     select : function(index, scrollIntoView){
51090         Roo.log('select ');
51091         return  ;
51092         
51093         this.selectedIndex = index;
51094         this.view.select(index);
51095         if(scrollIntoView !== false){
51096             var el = this.view.getNode(index);
51097             if(el){
51098                 this.innerList.scrollChildIntoView(el, false);
51099             }
51100         }
51101     },
51102
51103       
51104
51105     // private
51106     validateBlur : function(){
51107         
51108         return;
51109         
51110     },
51111
51112     // private
51113     initQuery : function(){
51114         this.doQuery(this.getRawValue());
51115     },
51116
51117     // private
51118     doForce : function(){
51119         if(this.el.dom.value.length > 0){
51120             this.el.dom.value =
51121                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51122              
51123         }
51124     },
51125
51126     /**
51127      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51128      * query allowing the query action to be canceled if needed.
51129      * @param {String} query The SQL query to execute
51130      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51131      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51132      * saved in the current store (defaults to false)
51133      */
51134     doQuery : function(q, forceAll){
51135         
51136         Roo.log('doQuery?');
51137         if(q === undefined || q === null){
51138             q = '';
51139         }
51140         var qe = {
51141             query: q,
51142             forceAll: forceAll,
51143             combo: this,
51144             cancel:false
51145         };
51146         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51147             return false;
51148         }
51149         q = qe.query;
51150         forceAll = qe.forceAll;
51151         if(forceAll === true || (q.length >= this.minChars)){
51152             if(this.lastQuery != q || this.alwaysQuery){
51153                 this.lastQuery = q;
51154                 if(this.mode == 'local'){
51155                     this.selectedIndex = -1;
51156                     if(forceAll){
51157                         this.store.clearFilter();
51158                     }else{
51159                         this.store.filter(this.displayField, q);
51160                     }
51161                     this.onLoad();
51162                 }else{
51163                     this.store.baseParams[this.queryParam] = q;
51164                     this.store.load({
51165                         params: this.getParams(q)
51166                     });
51167                     this.expand();
51168                 }
51169             }else{
51170                 this.selectedIndex = -1;
51171                 this.onLoad();   
51172             }
51173         }
51174     },
51175
51176     // private
51177     getParams : function(q){
51178         var p = {};
51179         //p[this.queryParam] = q;
51180         if(this.pageSize){
51181             p.start = 0;
51182             p.limit = this.pageSize;
51183         }
51184         return p;
51185     },
51186
51187     /**
51188      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51189      */
51190     collapse : function(){
51191         
51192     },
51193
51194     // private
51195     collapseIf : function(e){
51196         
51197     },
51198
51199     /**
51200      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51201      */
51202     expand : function(){
51203         
51204     } ,
51205
51206     // private
51207      
51208
51209     /** 
51210     * @cfg {Boolean} grow 
51211     * @hide 
51212     */
51213     /** 
51214     * @cfg {Number} growMin 
51215     * @hide 
51216     */
51217     /** 
51218     * @cfg {Number} growMax 
51219     * @hide 
51220     */
51221     /**
51222      * @hide
51223      * @method autoSize
51224      */
51225     
51226     setWidth : function()
51227     {
51228         
51229     },
51230     getResizeEl : function(){
51231         return this.el;
51232     }
51233 });//<script type="text/javasscript">
51234  
51235
51236 /**
51237  * @class Roo.DDView
51238  * A DnD enabled version of Roo.View.
51239  * @param {Element/String} container The Element in which to create the View.
51240  * @param {String} tpl The template string used to create the markup for each element of the View
51241  * @param {Object} config The configuration properties. These include all the config options of
51242  * {@link Roo.View} plus some specific to this class.<br>
51243  * <p>
51244  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51245  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51246  * <p>
51247  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51248 .x-view-drag-insert-above {
51249         border-top:1px dotted #3366cc;
51250 }
51251 .x-view-drag-insert-below {
51252         border-bottom:1px dotted #3366cc;
51253 }
51254 </code></pre>
51255  * 
51256  */
51257  
51258 Roo.DDView = function(container, tpl, config) {
51259     Roo.DDView.superclass.constructor.apply(this, arguments);
51260     this.getEl().setStyle("outline", "0px none");
51261     this.getEl().unselectable();
51262     if (this.dragGroup) {
51263                 this.setDraggable(this.dragGroup.split(","));
51264     }
51265     if (this.dropGroup) {
51266                 this.setDroppable(this.dropGroup.split(","));
51267     }
51268     if (this.deletable) {
51269         this.setDeletable();
51270     }
51271     this.isDirtyFlag = false;
51272         this.addEvents({
51273                 "drop" : true
51274         });
51275 };
51276
51277 Roo.extend(Roo.DDView, Roo.View, {
51278 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51279 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51280 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51281 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51282
51283         isFormField: true,
51284
51285         reset: Roo.emptyFn,
51286         
51287         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51288
51289         validate: function() {
51290                 return true;
51291         },
51292         
51293         destroy: function() {
51294                 this.purgeListeners();
51295                 this.getEl.removeAllListeners();
51296                 this.getEl().remove();
51297                 if (this.dragZone) {
51298                         if (this.dragZone.destroy) {
51299                                 this.dragZone.destroy();
51300                         }
51301                 }
51302                 if (this.dropZone) {
51303                         if (this.dropZone.destroy) {
51304                                 this.dropZone.destroy();
51305                         }
51306                 }
51307         },
51308
51309 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51310         getName: function() {
51311                 return this.name;
51312         },
51313
51314 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51315         setValue: function(v) {
51316                 if (!this.store) {
51317                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51318                 }
51319                 var data = {};
51320                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51321                 this.store.proxy = new Roo.data.MemoryProxy(data);
51322                 this.store.load();
51323         },
51324
51325 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51326         getValue: function() {
51327                 var result = '(';
51328                 this.store.each(function(rec) {
51329                         result += rec.id + ',';
51330                 });
51331                 return result.substr(0, result.length - 1) + ')';
51332         },
51333         
51334         getIds: function() {
51335                 var i = 0, result = new Array(this.store.getCount());
51336                 this.store.each(function(rec) {
51337                         result[i++] = rec.id;
51338                 });
51339                 return result;
51340         },
51341         
51342         isDirty: function() {
51343                 return this.isDirtyFlag;
51344         },
51345
51346 /**
51347  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51348  *      whole Element becomes the target, and this causes the drop gesture to append.
51349  */
51350     getTargetFromEvent : function(e) {
51351                 var target = e.getTarget();
51352                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51353                 target = target.parentNode;
51354                 }
51355                 if (!target) {
51356                         target = this.el.dom.lastChild || this.el.dom;
51357                 }
51358                 return target;
51359     },
51360
51361 /**
51362  *      Create the drag data which consists of an object which has the property "ddel" as
51363  *      the drag proxy element. 
51364  */
51365     getDragData : function(e) {
51366         var target = this.findItemFromChild(e.getTarget());
51367                 if(target) {
51368                         this.handleSelection(e);
51369                         var selNodes = this.getSelectedNodes();
51370             var dragData = {
51371                 source: this,
51372                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51373                 nodes: selNodes,
51374                 records: []
51375                         };
51376                         var selectedIndices = this.getSelectedIndexes();
51377                         for (var i = 0; i < selectedIndices.length; i++) {
51378                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51379                         }
51380                         if (selNodes.length == 1) {
51381                                 dragData.ddel = target.cloneNode(true); // the div element
51382                         } else {
51383                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51384                                 div.className = 'multi-proxy';
51385                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51386                                         div.appendChild(selNodes[i].cloneNode(true));
51387                                 }
51388                                 dragData.ddel = div;
51389                         }
51390             //console.log(dragData)
51391             //console.log(dragData.ddel.innerHTML)
51392                         return dragData;
51393                 }
51394         //console.log('nodragData')
51395                 return false;
51396     },
51397     
51398 /**     Specify to which ddGroup items in this DDView may be dragged. */
51399     setDraggable: function(ddGroup) {
51400         if (ddGroup instanceof Array) {
51401                 Roo.each(ddGroup, this.setDraggable, this);
51402                 return;
51403         }
51404         if (this.dragZone) {
51405                 this.dragZone.addToGroup(ddGroup);
51406         } else {
51407                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51408                                 containerScroll: true,
51409                                 ddGroup: ddGroup 
51410
51411                         });
51412 //                      Draggability implies selection. DragZone's mousedown selects the element.
51413                         if (!this.multiSelect) { this.singleSelect = true; }
51414
51415 //                      Wire the DragZone's handlers up to methods in *this*
51416                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51417                 }
51418     },
51419
51420 /**     Specify from which ddGroup this DDView accepts drops. */
51421     setDroppable: function(ddGroup) {
51422         if (ddGroup instanceof Array) {
51423                 Roo.each(ddGroup, this.setDroppable, this);
51424                 return;
51425         }
51426         if (this.dropZone) {
51427                 this.dropZone.addToGroup(ddGroup);
51428         } else {
51429                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51430                                 containerScroll: true,
51431                                 ddGroup: ddGroup
51432                         });
51433
51434 //                      Wire the DropZone's handlers up to methods in *this*
51435                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51436                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51437                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51438                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51439                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51440                 }
51441     },
51442
51443 /**     Decide whether to drop above or below a View node. */
51444     getDropPoint : function(e, n, dd){
51445         if (n == this.el.dom) { return "above"; }
51446                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51447                 var c = t + (b - t) / 2;
51448                 var y = Roo.lib.Event.getPageY(e);
51449                 if(y <= c) {
51450                         return "above";
51451                 }else{
51452                         return "below";
51453                 }
51454     },
51455
51456     onNodeEnter : function(n, dd, e, data){
51457                 return false;
51458     },
51459     
51460     onNodeOver : function(n, dd, e, data){
51461                 var pt = this.getDropPoint(e, n, dd);
51462                 // set the insert point style on the target node
51463                 var dragElClass = this.dropNotAllowed;
51464                 if (pt) {
51465                         var targetElClass;
51466                         if (pt == "above"){
51467                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51468                                 targetElClass = "x-view-drag-insert-above";
51469                         } else {
51470                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51471                                 targetElClass = "x-view-drag-insert-below";
51472                         }
51473                         if (this.lastInsertClass != targetElClass){
51474                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51475                                 this.lastInsertClass = targetElClass;
51476                         }
51477                 }
51478                 return dragElClass;
51479         },
51480
51481     onNodeOut : function(n, dd, e, data){
51482                 this.removeDropIndicators(n);
51483     },
51484
51485     onNodeDrop : function(n, dd, e, data){
51486         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51487                 return false;
51488         }
51489         var pt = this.getDropPoint(e, n, dd);
51490                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51491                 if (pt == "below") { insertAt++; }
51492                 for (var i = 0; i < data.records.length; i++) {
51493                         var r = data.records[i];
51494                         var dup = this.store.getById(r.id);
51495                         if (dup && (dd != this.dragZone)) {
51496                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51497                         } else {
51498                                 if (data.copy) {
51499                                         this.store.insert(insertAt++, r.copy());
51500                                 } else {
51501                                         data.source.isDirtyFlag = true;
51502                                         r.store.remove(r);
51503                                         this.store.insert(insertAt++, r);
51504                                 }
51505                                 this.isDirtyFlag = true;
51506                         }
51507                 }
51508                 this.dragZone.cachedTarget = null;
51509                 return true;
51510     },
51511
51512     removeDropIndicators : function(n){
51513                 if(n){
51514                         Roo.fly(n).removeClass([
51515                                 "x-view-drag-insert-above",
51516                                 "x-view-drag-insert-below"]);
51517                         this.lastInsertClass = "_noclass";
51518                 }
51519     },
51520
51521 /**
51522  *      Utility method. Add a delete option to the DDView's context menu.
51523  *      @param {String} imageUrl The URL of the "delete" icon image.
51524  */
51525         setDeletable: function(imageUrl) {
51526                 if (!this.singleSelect && !this.multiSelect) {
51527                         this.singleSelect = true;
51528                 }
51529                 var c = this.getContextMenu();
51530                 this.contextMenu.on("itemclick", function(item) {
51531                         switch (item.id) {
51532                                 case "delete":
51533                                         this.remove(this.getSelectedIndexes());
51534                                         break;
51535                         }
51536                 }, this);
51537                 this.contextMenu.add({
51538                         icon: imageUrl,
51539                         id: "delete",
51540                         text: 'Delete'
51541                 });
51542         },
51543         
51544 /**     Return the context menu for this DDView. */
51545         getContextMenu: function() {
51546                 if (!this.contextMenu) {
51547 //                      Create the View's context menu
51548                         this.contextMenu = new Roo.menu.Menu({
51549                                 id: this.id + "-contextmenu"
51550                         });
51551                         this.el.on("contextmenu", this.showContextMenu, this);
51552                 }
51553                 return this.contextMenu;
51554         },
51555         
51556         disableContextMenu: function() {
51557                 if (this.contextMenu) {
51558                         this.el.un("contextmenu", this.showContextMenu, this);
51559                 }
51560         },
51561
51562         showContextMenu: function(e, item) {
51563         item = this.findItemFromChild(e.getTarget());
51564                 if (item) {
51565                         e.stopEvent();
51566                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51567                         this.contextMenu.showAt(e.getXY());
51568             }
51569     },
51570
51571 /**
51572  *      Remove {@link Roo.data.Record}s at the specified indices.
51573  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51574  */
51575     remove: function(selectedIndices) {
51576                 selectedIndices = [].concat(selectedIndices);
51577                 for (var i = 0; i < selectedIndices.length; i++) {
51578                         var rec = this.store.getAt(selectedIndices[i]);
51579                         this.store.remove(rec);
51580                 }
51581     },
51582
51583 /**
51584  *      Double click fires the event, but also, if this is draggable, and there is only one other
51585  *      related DropZone, it transfers the selected node.
51586  */
51587     onDblClick : function(e){
51588         var item = this.findItemFromChild(e.getTarget());
51589         if(item){
51590             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51591                 return false;
51592             }
51593             if (this.dragGroup) {
51594                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51595                     while (targets.indexOf(this.dropZone) > -1) {
51596                             targets.remove(this.dropZone);
51597                                 }
51598                     if (targets.length == 1) {
51599                                         this.dragZone.cachedTarget = null;
51600                         var el = Roo.get(targets[0].getEl());
51601                         var box = el.getBox(true);
51602                         targets[0].onNodeDrop(el.dom, {
51603                                 target: el.dom,
51604                                 xy: [box.x, box.y + box.height - 1]
51605                         }, null, this.getDragData(e));
51606                     }
51607                 }
51608         }
51609     },
51610     
51611     handleSelection: function(e) {
51612                 this.dragZone.cachedTarget = null;
51613         var item = this.findItemFromChild(e.getTarget());
51614         if (!item) {
51615                 this.clearSelections(true);
51616                 return;
51617         }
51618                 if (item && (this.multiSelect || this.singleSelect)){
51619                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51620                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51621                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51622                                 this.unselect(item);
51623                         } else {
51624                                 this.select(item, this.multiSelect && e.ctrlKey);
51625                                 this.lastSelection = item;
51626                         }
51627                 }
51628     },
51629
51630     onItemClick : function(item, index, e){
51631                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51632                         return false;
51633                 }
51634                 return true;
51635     },
51636
51637     unselect : function(nodeInfo, suppressEvent){
51638                 var node = this.getNode(nodeInfo);
51639                 if(node && this.isSelected(node)){
51640                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51641                                 Roo.fly(node).removeClass(this.selectedClass);
51642                                 this.selections.remove(node);
51643                                 if(!suppressEvent){
51644                                         this.fireEvent("selectionchange", this, this.selections);
51645                                 }
51646                         }
51647                 }
51648     }
51649 });
51650 /*
51651  * Based on:
51652  * Ext JS Library 1.1.1
51653  * Copyright(c) 2006-2007, Ext JS, LLC.
51654  *
51655  * Originally Released Under LGPL - original licence link has changed is not relivant.
51656  *
51657  * Fork - LGPL
51658  * <script type="text/javascript">
51659  */
51660  
51661 /**
51662  * @class Roo.LayoutManager
51663  * @extends Roo.util.Observable
51664  * Base class for layout managers.
51665  */
51666 Roo.LayoutManager = function(container, config){
51667     Roo.LayoutManager.superclass.constructor.call(this);
51668     this.el = Roo.get(container);
51669     // ie scrollbar fix
51670     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51671         document.body.scroll = "no";
51672     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51673         this.el.position('relative');
51674     }
51675     this.id = this.el.id;
51676     this.el.addClass("x-layout-container");
51677     /** false to disable window resize monitoring @type Boolean */
51678     this.monitorWindowResize = true;
51679     this.regions = {};
51680     this.addEvents({
51681         /**
51682          * @event layout
51683          * Fires when a layout is performed. 
51684          * @param {Roo.LayoutManager} this
51685          */
51686         "layout" : true,
51687         /**
51688          * @event regionresized
51689          * Fires when the user resizes a region. 
51690          * @param {Roo.LayoutRegion} region The resized region
51691          * @param {Number} newSize The new size (width for east/west, height for north/south)
51692          */
51693         "regionresized" : true,
51694         /**
51695          * @event regioncollapsed
51696          * Fires when a region is collapsed. 
51697          * @param {Roo.LayoutRegion} region The collapsed region
51698          */
51699         "regioncollapsed" : true,
51700         /**
51701          * @event regionexpanded
51702          * Fires when a region is expanded.  
51703          * @param {Roo.LayoutRegion} region The expanded region
51704          */
51705         "regionexpanded" : true
51706     });
51707     this.updating = false;
51708     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51709 };
51710
51711 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51712     /**
51713      * Returns true if this layout is currently being updated
51714      * @return {Boolean}
51715      */
51716     isUpdating : function(){
51717         return this.updating; 
51718     },
51719     
51720     /**
51721      * Suspend the LayoutManager from doing auto-layouts while
51722      * making multiple add or remove calls
51723      */
51724     beginUpdate : function(){
51725         this.updating = true;    
51726     },
51727     
51728     /**
51729      * Restore auto-layouts and optionally disable the manager from performing a layout
51730      * @param {Boolean} noLayout true to disable a layout update 
51731      */
51732     endUpdate : function(noLayout){
51733         this.updating = false;
51734         if(!noLayout){
51735             this.layout();
51736         }    
51737     },
51738     
51739     layout: function(){
51740         
51741     },
51742     
51743     onRegionResized : function(region, newSize){
51744         this.fireEvent("regionresized", region, newSize);
51745         this.layout();
51746     },
51747     
51748     onRegionCollapsed : function(region){
51749         this.fireEvent("regioncollapsed", region);
51750     },
51751     
51752     onRegionExpanded : function(region){
51753         this.fireEvent("regionexpanded", region);
51754     },
51755         
51756     /**
51757      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51758      * performs box-model adjustments.
51759      * @return {Object} The size as an object {width: (the width), height: (the height)}
51760      */
51761     getViewSize : function(){
51762         var size;
51763         if(this.el.dom != document.body){
51764             size = this.el.getSize();
51765         }else{
51766             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51767         }
51768         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51769         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51770         return size;
51771     },
51772     
51773     /**
51774      * Returns the Element this layout is bound to.
51775      * @return {Roo.Element}
51776      */
51777     getEl : function(){
51778         return this.el;
51779     },
51780     
51781     /**
51782      * Returns the specified region.
51783      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51784      * @return {Roo.LayoutRegion}
51785      */
51786     getRegion : function(target){
51787         return this.regions[target.toLowerCase()];
51788     },
51789     
51790     onWindowResize : function(){
51791         if(this.monitorWindowResize){
51792             this.layout();
51793         }
51794     }
51795 });/*
51796  * Based on:
51797  * Ext JS Library 1.1.1
51798  * Copyright(c) 2006-2007, Ext JS, LLC.
51799  *
51800  * Originally Released Under LGPL - original licence link has changed is not relivant.
51801  *
51802  * Fork - LGPL
51803  * <script type="text/javascript">
51804  */
51805 /**
51806  * @class Roo.BorderLayout
51807  * @extends Roo.LayoutManager
51808  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51809  * please see: <br><br>
51810  * <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>
51811  * <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>
51812  * Example:
51813  <pre><code>
51814  var layout = new Roo.BorderLayout(document.body, {
51815     north: {
51816         initialSize: 25,
51817         titlebar: false
51818     },
51819     west: {
51820         split:true,
51821         initialSize: 200,
51822         minSize: 175,
51823         maxSize: 400,
51824         titlebar: true,
51825         collapsible: true
51826     },
51827     east: {
51828         split:true,
51829         initialSize: 202,
51830         minSize: 175,
51831         maxSize: 400,
51832         titlebar: true,
51833         collapsible: true
51834     },
51835     south: {
51836         split:true,
51837         initialSize: 100,
51838         minSize: 100,
51839         maxSize: 200,
51840         titlebar: true,
51841         collapsible: true
51842     },
51843     center: {
51844         titlebar: true,
51845         autoScroll:true,
51846         resizeTabs: true,
51847         minTabWidth: 50,
51848         preferredTabWidth: 150
51849     }
51850 });
51851
51852 // shorthand
51853 var CP = Roo.ContentPanel;
51854
51855 layout.beginUpdate();
51856 layout.add("north", new CP("north", "North"));
51857 layout.add("south", new CP("south", {title: "South", closable: true}));
51858 layout.add("west", new CP("west", {title: "West"}));
51859 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51860 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51861 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51862 layout.getRegion("center").showPanel("center1");
51863 layout.endUpdate();
51864 </code></pre>
51865
51866 <b>The container the layout is rendered into can be either the body element or any other element.
51867 If it is not the body element, the container needs to either be an absolute positioned element,
51868 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51869 the container size if it is not the body element.</b>
51870
51871 * @constructor
51872 * Create a new BorderLayout
51873 * @param {String/HTMLElement/Element} container The container this layout is bound to
51874 * @param {Object} config Configuration options
51875  */
51876 Roo.BorderLayout = function(container, config){
51877     config = config || {};
51878     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51879     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51880     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51881         var target = this.factory.validRegions[i];
51882         if(config[target]){
51883             this.addRegion(target, config[target]);
51884         }
51885     }
51886 };
51887
51888 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51889     /**
51890      * Creates and adds a new region if it doesn't already exist.
51891      * @param {String} target The target region key (north, south, east, west or center).
51892      * @param {Object} config The regions config object
51893      * @return {BorderLayoutRegion} The new region
51894      */
51895     addRegion : function(target, config){
51896         if(!this.regions[target]){
51897             var r = this.factory.create(target, this, config);
51898             this.bindRegion(target, r);
51899         }
51900         return this.regions[target];
51901     },
51902
51903     // private (kinda)
51904     bindRegion : function(name, r){
51905         this.regions[name] = r;
51906         r.on("visibilitychange", this.layout, this);
51907         r.on("paneladded", this.layout, this);
51908         r.on("panelremoved", this.layout, this);
51909         r.on("invalidated", this.layout, this);
51910         r.on("resized", this.onRegionResized, this);
51911         r.on("collapsed", this.onRegionCollapsed, this);
51912         r.on("expanded", this.onRegionExpanded, this);
51913     },
51914
51915     /**
51916      * Performs a layout update.
51917      */
51918     layout : function(){
51919         if(this.updating) {
51920             return;
51921         }
51922         var size = this.getViewSize();
51923         var w = size.width;
51924         var h = size.height;
51925         var centerW = w;
51926         var centerH = h;
51927         var centerY = 0;
51928         var centerX = 0;
51929         //var x = 0, y = 0;
51930
51931         var rs = this.regions;
51932         var north = rs["north"];
51933         var south = rs["south"]; 
51934         var west = rs["west"];
51935         var east = rs["east"];
51936         var center = rs["center"];
51937         //if(this.hideOnLayout){ // not supported anymore
51938             //c.el.setStyle("display", "none");
51939         //}
51940         if(north && north.isVisible()){
51941             var b = north.getBox();
51942             var m = north.getMargins();
51943             b.width = w - (m.left+m.right);
51944             b.x = m.left;
51945             b.y = m.top;
51946             centerY = b.height + b.y + m.bottom;
51947             centerH -= centerY;
51948             north.updateBox(this.safeBox(b));
51949         }
51950         if(south && south.isVisible()){
51951             var b = south.getBox();
51952             var m = south.getMargins();
51953             b.width = w - (m.left+m.right);
51954             b.x = m.left;
51955             var totalHeight = (b.height + m.top + m.bottom);
51956             b.y = h - totalHeight + m.top;
51957             centerH -= totalHeight;
51958             south.updateBox(this.safeBox(b));
51959         }
51960         if(west && west.isVisible()){
51961             var b = west.getBox();
51962             var m = west.getMargins();
51963             b.height = centerH - (m.top+m.bottom);
51964             b.x = m.left;
51965             b.y = centerY + m.top;
51966             var totalWidth = (b.width + m.left + m.right);
51967             centerX += totalWidth;
51968             centerW -= totalWidth;
51969             west.updateBox(this.safeBox(b));
51970         }
51971         if(east && east.isVisible()){
51972             var b = east.getBox();
51973             var m = east.getMargins();
51974             b.height = centerH - (m.top+m.bottom);
51975             var totalWidth = (b.width + m.left + m.right);
51976             b.x = w - totalWidth + m.left;
51977             b.y = centerY + m.top;
51978             centerW -= totalWidth;
51979             east.updateBox(this.safeBox(b));
51980         }
51981         if(center){
51982             var m = center.getMargins();
51983             var centerBox = {
51984                 x: centerX + m.left,
51985                 y: centerY + m.top,
51986                 width: centerW - (m.left+m.right),
51987                 height: centerH - (m.top+m.bottom)
51988             };
51989             //if(this.hideOnLayout){
51990                 //center.el.setStyle("display", "block");
51991             //}
51992             center.updateBox(this.safeBox(centerBox));
51993         }
51994         this.el.repaint();
51995         this.fireEvent("layout", this);
51996     },
51997
51998     // private
51999     safeBox : function(box){
52000         box.width = Math.max(0, box.width);
52001         box.height = Math.max(0, box.height);
52002         return box;
52003     },
52004
52005     /**
52006      * Adds a ContentPanel (or subclass) to this layout.
52007      * @param {String} target The target region key (north, south, east, west or center).
52008      * @param {Roo.ContentPanel} panel The panel to add
52009      * @return {Roo.ContentPanel} The added panel
52010      */
52011     add : function(target, panel){
52012          
52013         target = target.toLowerCase();
52014         return this.regions[target].add(panel);
52015     },
52016
52017     /**
52018      * Remove a ContentPanel (or subclass) to this layout.
52019      * @param {String} target The target region key (north, south, east, west or center).
52020      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52021      * @return {Roo.ContentPanel} The removed panel
52022      */
52023     remove : function(target, panel){
52024         target = target.toLowerCase();
52025         return this.regions[target].remove(panel);
52026     },
52027
52028     /**
52029      * Searches all regions for a panel with the specified id
52030      * @param {String} panelId
52031      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52032      */
52033     findPanel : function(panelId){
52034         var rs = this.regions;
52035         for(var target in rs){
52036             if(typeof rs[target] != "function"){
52037                 var p = rs[target].getPanel(panelId);
52038                 if(p){
52039                     return p;
52040                 }
52041             }
52042         }
52043         return null;
52044     },
52045
52046     /**
52047      * Searches all regions for a panel with the specified id and activates (shows) it.
52048      * @param {String/ContentPanel} panelId The panels id or the panel itself
52049      * @return {Roo.ContentPanel} The shown panel or null
52050      */
52051     showPanel : function(panelId) {
52052       var rs = this.regions;
52053       for(var target in rs){
52054          var r = rs[target];
52055          if(typeof r != "function"){
52056             if(r.hasPanel(panelId)){
52057                return r.showPanel(panelId);
52058             }
52059          }
52060       }
52061       return null;
52062    },
52063
52064    /**
52065      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52066      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52067      */
52068     restoreState : function(provider){
52069         if(!provider){
52070             provider = Roo.state.Manager;
52071         }
52072         var sm = new Roo.LayoutStateManager();
52073         sm.init(this, provider);
52074     },
52075
52076     /**
52077      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52078      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52079      * a valid ContentPanel config object.  Example:
52080      * <pre><code>
52081 // Create the main layout
52082 var layout = new Roo.BorderLayout('main-ct', {
52083     west: {
52084         split:true,
52085         minSize: 175,
52086         titlebar: true
52087     },
52088     center: {
52089         title:'Components'
52090     }
52091 }, 'main-ct');
52092
52093 // Create and add multiple ContentPanels at once via configs
52094 layout.batchAdd({
52095    west: {
52096        id: 'source-files',
52097        autoCreate:true,
52098        title:'Ext Source Files',
52099        autoScroll:true,
52100        fitToFrame:true
52101    },
52102    center : {
52103        el: cview,
52104        autoScroll:true,
52105        fitToFrame:true,
52106        toolbar: tb,
52107        resizeEl:'cbody'
52108    }
52109 });
52110 </code></pre>
52111      * @param {Object} regions An object containing ContentPanel configs by region name
52112      */
52113     batchAdd : function(regions){
52114         this.beginUpdate();
52115         for(var rname in regions){
52116             var lr = this.regions[rname];
52117             if(lr){
52118                 this.addTypedPanels(lr, regions[rname]);
52119             }
52120         }
52121         this.endUpdate();
52122     },
52123
52124     // private
52125     addTypedPanels : function(lr, ps){
52126         if(typeof ps == 'string'){
52127             lr.add(new Roo.ContentPanel(ps));
52128         }
52129         else if(ps instanceof Array){
52130             for(var i =0, len = ps.length; i < len; i++){
52131                 this.addTypedPanels(lr, ps[i]);
52132             }
52133         }
52134         else if(!ps.events){ // raw config?
52135             var el = ps.el;
52136             delete ps.el; // prevent conflict
52137             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52138         }
52139         else {  // panel object assumed!
52140             lr.add(ps);
52141         }
52142     },
52143     /**
52144      * Adds a xtype elements to the layout.
52145      * <pre><code>
52146
52147 layout.addxtype({
52148        xtype : 'ContentPanel',
52149        region: 'west',
52150        items: [ .... ]
52151    }
52152 );
52153
52154 layout.addxtype({
52155         xtype : 'NestedLayoutPanel',
52156         region: 'west',
52157         layout: {
52158            center: { },
52159            west: { }   
52160         },
52161         items : [ ... list of content panels or nested layout panels.. ]
52162    }
52163 );
52164 </code></pre>
52165      * @param {Object} cfg Xtype definition of item to add.
52166      */
52167     addxtype : function(cfg)
52168     {
52169         // basically accepts a pannel...
52170         // can accept a layout region..!?!?
52171         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52172         
52173         if (!cfg.xtype.match(/Panel$/)) {
52174             return false;
52175         }
52176         var ret = false;
52177         
52178         if (typeof(cfg.region) == 'undefined') {
52179             Roo.log("Failed to add Panel, region was not set");
52180             Roo.log(cfg);
52181             return false;
52182         }
52183         var region = cfg.region;
52184         delete cfg.region;
52185         
52186           
52187         var xitems = [];
52188         if (cfg.items) {
52189             xitems = cfg.items;
52190             delete cfg.items;
52191         }
52192         var nb = false;
52193         
52194         switch(cfg.xtype) 
52195         {
52196             case 'ContentPanel':  // ContentPanel (el, cfg)
52197             case 'ScrollPanel':  // ContentPanel (el, cfg)
52198             case 'ViewPanel': 
52199                 if(cfg.autoCreate) {
52200                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52201                 } else {
52202                     var el = this.el.createChild();
52203                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52204                 }
52205                 
52206                 this.add(region, ret);
52207                 break;
52208             
52209             
52210             case 'TreePanel': // our new panel!
52211                 cfg.el = this.el.createChild();
52212                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52213                 this.add(region, ret);
52214                 break;
52215             
52216             case 'NestedLayoutPanel': 
52217                 // create a new Layout (which is  a Border Layout...
52218                 var el = this.el.createChild();
52219                 var clayout = cfg.layout;
52220                 delete cfg.layout;
52221                 clayout.items   = clayout.items  || [];
52222                 // replace this exitems with the clayout ones..
52223                 xitems = clayout.items;
52224                  
52225                 
52226                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52227                     cfg.background = false;
52228                 }
52229                 var layout = new Roo.BorderLayout(el, clayout);
52230                 
52231                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52232                 //console.log('adding nested layout panel '  + cfg.toSource());
52233                 this.add(region, ret);
52234                 nb = {}; /// find first...
52235                 break;
52236                 
52237             case 'GridPanel': 
52238             
52239                 // needs grid and region
52240                 
52241                 //var el = this.getRegion(region).el.createChild();
52242                 var el = this.el.createChild();
52243                 // create the grid first...
52244                 
52245                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52246                 delete cfg.grid;
52247                 if (region == 'center' && this.active ) {
52248                     cfg.background = false;
52249                 }
52250                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52251                 
52252                 this.add(region, ret);
52253                 if (cfg.background) {
52254                     ret.on('activate', function(gp) {
52255                         if (!gp.grid.rendered) {
52256                             gp.grid.render();
52257                         }
52258                     });
52259                 } else {
52260                     grid.render();
52261                 }
52262                 break;
52263            
52264            
52265            
52266                 
52267                 
52268                 
52269             default:
52270                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52271                     
52272                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52273                     this.add(region, ret);
52274                 } else {
52275                 
52276                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52277                     return null;
52278                 }
52279                 
52280              // GridPanel (grid, cfg)
52281             
52282         }
52283         this.beginUpdate();
52284         // add children..
52285         var region = '';
52286         var abn = {};
52287         Roo.each(xitems, function(i)  {
52288             region = nb && i.region ? i.region : false;
52289             
52290             var add = ret.addxtype(i);
52291            
52292             if (region) {
52293                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52294                 if (!i.background) {
52295                     abn[region] = nb[region] ;
52296                 }
52297             }
52298             
52299         });
52300         this.endUpdate();
52301
52302         // make the last non-background panel active..
52303         //if (nb) { Roo.log(abn); }
52304         if (nb) {
52305             
52306             for(var r in abn) {
52307                 region = this.getRegion(r);
52308                 if (region) {
52309                     // tried using nb[r], but it does not work..
52310                      
52311                     region.showPanel(abn[r]);
52312                    
52313                 }
52314             }
52315         }
52316         return ret;
52317         
52318     }
52319 });
52320
52321 /**
52322  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52323  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52324  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52325  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52326  * <pre><code>
52327 // shorthand
52328 var CP = Roo.ContentPanel;
52329
52330 var layout = Roo.BorderLayout.create({
52331     north: {
52332         initialSize: 25,
52333         titlebar: false,
52334         panels: [new CP("north", "North")]
52335     },
52336     west: {
52337         split:true,
52338         initialSize: 200,
52339         minSize: 175,
52340         maxSize: 400,
52341         titlebar: true,
52342         collapsible: true,
52343         panels: [new CP("west", {title: "West"})]
52344     },
52345     east: {
52346         split:true,
52347         initialSize: 202,
52348         minSize: 175,
52349         maxSize: 400,
52350         titlebar: true,
52351         collapsible: true,
52352         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52353     },
52354     south: {
52355         split:true,
52356         initialSize: 100,
52357         minSize: 100,
52358         maxSize: 200,
52359         titlebar: true,
52360         collapsible: true,
52361         panels: [new CP("south", {title: "South", closable: true})]
52362     },
52363     center: {
52364         titlebar: true,
52365         autoScroll:true,
52366         resizeTabs: true,
52367         minTabWidth: 50,
52368         preferredTabWidth: 150,
52369         panels: [
52370             new CP("center1", {title: "Close Me", closable: true}),
52371             new CP("center2", {title: "Center Panel", closable: false})
52372         ]
52373     }
52374 }, document.body);
52375
52376 layout.getRegion("center").showPanel("center1");
52377 </code></pre>
52378  * @param config
52379  * @param targetEl
52380  */
52381 Roo.BorderLayout.create = function(config, targetEl){
52382     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52383     layout.beginUpdate();
52384     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52385     for(var j = 0, jlen = regions.length; j < jlen; j++){
52386         var lr = regions[j];
52387         if(layout.regions[lr] && config[lr].panels){
52388             var r = layout.regions[lr];
52389             var ps = config[lr].panels;
52390             layout.addTypedPanels(r, ps);
52391         }
52392     }
52393     layout.endUpdate();
52394     return layout;
52395 };
52396
52397 // private
52398 Roo.BorderLayout.RegionFactory = {
52399     // private
52400     validRegions : ["north","south","east","west","center"],
52401
52402     // private
52403     create : function(target, mgr, config){
52404         target = target.toLowerCase();
52405         if(config.lightweight || config.basic){
52406             return new Roo.BasicLayoutRegion(mgr, config, target);
52407         }
52408         switch(target){
52409             case "north":
52410                 return new Roo.NorthLayoutRegion(mgr, config);
52411             case "south":
52412                 return new Roo.SouthLayoutRegion(mgr, config);
52413             case "east":
52414                 return new Roo.EastLayoutRegion(mgr, config);
52415             case "west":
52416                 return new Roo.WestLayoutRegion(mgr, config);
52417             case "center":
52418                 return new Roo.CenterLayoutRegion(mgr, config);
52419         }
52420         throw 'Layout region "'+target+'" not supported.';
52421     }
52422 };/*
52423  * Based on:
52424  * Ext JS Library 1.1.1
52425  * Copyright(c) 2006-2007, Ext JS, LLC.
52426  *
52427  * Originally Released Under LGPL - original licence link has changed is not relivant.
52428  *
52429  * Fork - LGPL
52430  * <script type="text/javascript">
52431  */
52432  
52433 /**
52434  * @class Roo.BasicLayoutRegion
52435  * @extends Roo.util.Observable
52436  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52437  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52438  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52439  */
52440 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52441     this.mgr = mgr;
52442     this.position  = pos;
52443     this.events = {
52444         /**
52445          * @scope Roo.BasicLayoutRegion
52446          */
52447         
52448         /**
52449          * @event beforeremove
52450          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52451          * @param {Roo.LayoutRegion} this
52452          * @param {Roo.ContentPanel} panel The panel
52453          * @param {Object} e The cancel event object
52454          */
52455         "beforeremove" : true,
52456         /**
52457          * @event invalidated
52458          * Fires when the layout for this region is changed.
52459          * @param {Roo.LayoutRegion} this
52460          */
52461         "invalidated" : true,
52462         /**
52463          * @event visibilitychange
52464          * Fires when this region is shown or hidden 
52465          * @param {Roo.LayoutRegion} this
52466          * @param {Boolean} visibility true or false
52467          */
52468         "visibilitychange" : true,
52469         /**
52470          * @event paneladded
52471          * Fires when a panel is added. 
52472          * @param {Roo.LayoutRegion} this
52473          * @param {Roo.ContentPanel} panel The panel
52474          */
52475         "paneladded" : true,
52476         /**
52477          * @event panelremoved
52478          * Fires when a panel is removed. 
52479          * @param {Roo.LayoutRegion} this
52480          * @param {Roo.ContentPanel} panel The panel
52481          */
52482         "panelremoved" : true,
52483         /**
52484          * @event beforecollapse
52485          * Fires when this region before collapse.
52486          * @param {Roo.LayoutRegion} this
52487          */
52488         "beforecollapse" : true,
52489         /**
52490          * @event collapsed
52491          * Fires when this region is collapsed.
52492          * @param {Roo.LayoutRegion} this
52493          */
52494         "collapsed" : true,
52495         /**
52496          * @event expanded
52497          * Fires when this region is expanded.
52498          * @param {Roo.LayoutRegion} this
52499          */
52500         "expanded" : true,
52501         /**
52502          * @event slideshow
52503          * Fires when this region is slid into view.
52504          * @param {Roo.LayoutRegion} this
52505          */
52506         "slideshow" : true,
52507         /**
52508          * @event slidehide
52509          * Fires when this region slides out of view. 
52510          * @param {Roo.LayoutRegion} this
52511          */
52512         "slidehide" : true,
52513         /**
52514          * @event panelactivated
52515          * Fires when a panel is activated. 
52516          * @param {Roo.LayoutRegion} this
52517          * @param {Roo.ContentPanel} panel The activated panel
52518          */
52519         "panelactivated" : true,
52520         /**
52521          * @event resized
52522          * Fires when the user resizes this region. 
52523          * @param {Roo.LayoutRegion} this
52524          * @param {Number} newSize The new size (width for east/west, height for north/south)
52525          */
52526         "resized" : true
52527     };
52528     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52529     this.panels = new Roo.util.MixedCollection();
52530     this.panels.getKey = this.getPanelId.createDelegate(this);
52531     this.box = null;
52532     this.activePanel = null;
52533     // ensure listeners are added...
52534     
52535     if (config.listeners || config.events) {
52536         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52537             listeners : config.listeners || {},
52538             events : config.events || {}
52539         });
52540     }
52541     
52542     if(skipConfig !== true){
52543         this.applyConfig(config);
52544     }
52545 };
52546
52547 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52548     getPanelId : function(p){
52549         return p.getId();
52550     },
52551     
52552     applyConfig : function(config){
52553         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52554         this.config = config;
52555         
52556     },
52557     
52558     /**
52559      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52560      * the width, for horizontal (north, south) the height.
52561      * @param {Number} newSize The new width or height
52562      */
52563     resizeTo : function(newSize){
52564         var el = this.el ? this.el :
52565                  (this.activePanel ? this.activePanel.getEl() : null);
52566         if(el){
52567             switch(this.position){
52568                 case "east":
52569                 case "west":
52570                     el.setWidth(newSize);
52571                     this.fireEvent("resized", this, newSize);
52572                 break;
52573                 case "north":
52574                 case "south":
52575                     el.setHeight(newSize);
52576                     this.fireEvent("resized", this, newSize);
52577                 break;                
52578             }
52579         }
52580     },
52581     
52582     getBox : function(){
52583         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52584     },
52585     
52586     getMargins : function(){
52587         return this.margins;
52588     },
52589     
52590     updateBox : function(box){
52591         this.box = box;
52592         var el = this.activePanel.getEl();
52593         el.dom.style.left = box.x + "px";
52594         el.dom.style.top = box.y + "px";
52595         this.activePanel.setSize(box.width, box.height);
52596     },
52597     
52598     /**
52599      * Returns the container element for this region.
52600      * @return {Roo.Element}
52601      */
52602     getEl : function(){
52603         return this.activePanel;
52604     },
52605     
52606     /**
52607      * Returns true if this region is currently visible.
52608      * @return {Boolean}
52609      */
52610     isVisible : function(){
52611         return this.activePanel ? true : false;
52612     },
52613     
52614     setActivePanel : function(panel){
52615         panel = this.getPanel(panel);
52616         if(this.activePanel && this.activePanel != panel){
52617             this.activePanel.setActiveState(false);
52618             this.activePanel.getEl().setLeftTop(-10000,-10000);
52619         }
52620         this.activePanel = panel;
52621         panel.setActiveState(true);
52622         if(this.box){
52623             panel.setSize(this.box.width, this.box.height);
52624         }
52625         this.fireEvent("panelactivated", this, panel);
52626         this.fireEvent("invalidated");
52627     },
52628     
52629     /**
52630      * Show the specified panel.
52631      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52632      * @return {Roo.ContentPanel} The shown panel or null
52633      */
52634     showPanel : function(panel){
52635         if(panel = this.getPanel(panel)){
52636             this.setActivePanel(panel);
52637         }
52638         return panel;
52639     },
52640     
52641     /**
52642      * Get the active panel for this region.
52643      * @return {Roo.ContentPanel} The active panel or null
52644      */
52645     getActivePanel : function(){
52646         return this.activePanel;
52647     },
52648     
52649     /**
52650      * Add the passed ContentPanel(s)
52651      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52652      * @return {Roo.ContentPanel} The panel added (if only one was added)
52653      */
52654     add : function(panel){
52655         if(arguments.length > 1){
52656             for(var i = 0, len = arguments.length; i < len; i++) {
52657                 this.add(arguments[i]);
52658             }
52659             return null;
52660         }
52661         if(this.hasPanel(panel)){
52662             this.showPanel(panel);
52663             return panel;
52664         }
52665         var el = panel.getEl();
52666         if(el.dom.parentNode != this.mgr.el.dom){
52667             this.mgr.el.dom.appendChild(el.dom);
52668         }
52669         if(panel.setRegion){
52670             panel.setRegion(this);
52671         }
52672         this.panels.add(panel);
52673         el.setStyle("position", "absolute");
52674         if(!panel.background){
52675             this.setActivePanel(panel);
52676             if(this.config.initialSize && this.panels.getCount()==1){
52677                 this.resizeTo(this.config.initialSize);
52678             }
52679         }
52680         this.fireEvent("paneladded", this, panel);
52681         return panel;
52682     },
52683     
52684     /**
52685      * Returns true if the panel is in this region.
52686      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52687      * @return {Boolean}
52688      */
52689     hasPanel : function(panel){
52690         if(typeof panel == "object"){ // must be panel obj
52691             panel = panel.getId();
52692         }
52693         return this.getPanel(panel) ? true : false;
52694     },
52695     
52696     /**
52697      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52698      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52699      * @param {Boolean} preservePanel Overrides the config preservePanel option
52700      * @return {Roo.ContentPanel} The panel that was removed
52701      */
52702     remove : function(panel, preservePanel){
52703         panel = this.getPanel(panel);
52704         if(!panel){
52705             return null;
52706         }
52707         var e = {};
52708         this.fireEvent("beforeremove", this, panel, e);
52709         if(e.cancel === true){
52710             return null;
52711         }
52712         var panelId = panel.getId();
52713         this.panels.removeKey(panelId);
52714         return panel;
52715     },
52716     
52717     /**
52718      * Returns the panel specified or null if it's not in this region.
52719      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52720      * @return {Roo.ContentPanel}
52721      */
52722     getPanel : function(id){
52723         if(typeof id == "object"){ // must be panel obj
52724             return id;
52725         }
52726         return this.panels.get(id);
52727     },
52728     
52729     /**
52730      * Returns this regions position (north/south/east/west/center).
52731      * @return {String} 
52732      */
52733     getPosition: function(){
52734         return this.position;    
52735     }
52736 });/*
52737  * Based on:
52738  * Ext JS Library 1.1.1
52739  * Copyright(c) 2006-2007, Ext JS, LLC.
52740  *
52741  * Originally Released Under LGPL - original licence link has changed is not relivant.
52742  *
52743  * Fork - LGPL
52744  * <script type="text/javascript">
52745  */
52746  
52747 /**
52748  * @class Roo.LayoutRegion
52749  * @extends Roo.BasicLayoutRegion
52750  * This class represents a region in a layout manager.
52751  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52752  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52753  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52754  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52755  * @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})
52756  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52757  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52758  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52759  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52760  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52761  * @cfg {String}    title           The title for the region (overrides panel titles)
52762  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52763  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52764  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52765  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52766  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52767  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52768  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52769  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52770  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52771  * @cfg {Boolean}   showPin         True to show a pin button
52772  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52773  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52774  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52775  * @cfg {Number}    width           For East/West panels
52776  * @cfg {Number}    height          For North/South panels
52777  * @cfg {Boolean}   split           To show the splitter
52778  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52779  */
52780 Roo.LayoutRegion = function(mgr, config, pos){
52781     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52782     var dh = Roo.DomHelper;
52783     /** This region's container element 
52784     * @type Roo.Element */
52785     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52786     /** This region's title element 
52787     * @type Roo.Element */
52788
52789     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52790         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52791         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52792     ]}, true);
52793     this.titleEl.enableDisplayMode();
52794     /** This region's title text element 
52795     * @type HTMLElement */
52796     this.titleTextEl = this.titleEl.dom.firstChild;
52797     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52798     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52799     this.closeBtn.enableDisplayMode();
52800     this.closeBtn.on("click", this.closeClicked, this);
52801     this.closeBtn.hide();
52802
52803     this.createBody(config);
52804     this.visible = true;
52805     this.collapsed = false;
52806
52807     if(config.hideWhenEmpty){
52808         this.hide();
52809         this.on("paneladded", this.validateVisibility, this);
52810         this.on("panelremoved", this.validateVisibility, this);
52811     }
52812     this.applyConfig(config);
52813 };
52814
52815 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52816
52817     createBody : function(){
52818         /** This region's body element 
52819         * @type Roo.Element */
52820         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52821     },
52822
52823     applyConfig : function(c){
52824         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52825             var dh = Roo.DomHelper;
52826             if(c.titlebar !== false){
52827                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52828                 this.collapseBtn.on("click", this.collapse, this);
52829                 this.collapseBtn.enableDisplayMode();
52830
52831                 if(c.showPin === true || this.showPin){
52832                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52833                     this.stickBtn.enableDisplayMode();
52834                     this.stickBtn.on("click", this.expand, this);
52835                     this.stickBtn.hide();
52836                 }
52837             }
52838             /** This region's collapsed element
52839             * @type Roo.Element */
52840             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52841                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52842             ]}, true);
52843             if(c.floatable !== false){
52844                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52845                this.collapsedEl.on("click", this.collapseClick, this);
52846             }
52847
52848             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52849                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52850                    id: "message", unselectable: "on", style:{"float":"left"}});
52851                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52852              }
52853             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52854             this.expandBtn.on("click", this.expand, this);
52855         }
52856         if(this.collapseBtn){
52857             this.collapseBtn.setVisible(c.collapsible == true);
52858         }
52859         this.cmargins = c.cmargins || this.cmargins ||
52860                          (this.position == "west" || this.position == "east" ?
52861                              {top: 0, left: 2, right:2, bottom: 0} :
52862                              {top: 2, left: 0, right:0, bottom: 2});
52863         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52864         this.bottomTabs = c.tabPosition != "top";
52865         this.autoScroll = c.autoScroll || false;
52866         if(this.autoScroll){
52867             this.bodyEl.setStyle("overflow", "auto");
52868         }else{
52869             this.bodyEl.setStyle("overflow", "hidden");
52870         }
52871         //if(c.titlebar !== false){
52872             if((!c.titlebar && !c.title) || c.titlebar === false){
52873                 this.titleEl.hide();
52874             }else{
52875                 this.titleEl.show();
52876                 if(c.title){
52877                     this.titleTextEl.innerHTML = c.title;
52878                 }
52879             }
52880         //}
52881         this.duration = c.duration || .30;
52882         this.slideDuration = c.slideDuration || .45;
52883         this.config = c;
52884         if(c.collapsed){
52885             this.collapse(true);
52886         }
52887         if(c.hidden){
52888             this.hide();
52889         }
52890     },
52891     /**
52892      * Returns true if this region is currently visible.
52893      * @return {Boolean}
52894      */
52895     isVisible : function(){
52896         return this.visible;
52897     },
52898
52899     /**
52900      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52901      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52902      */
52903     setCollapsedTitle : function(title){
52904         title = title || "&#160;";
52905         if(this.collapsedTitleTextEl){
52906             this.collapsedTitleTextEl.innerHTML = title;
52907         }
52908     },
52909
52910     getBox : function(){
52911         var b;
52912         if(!this.collapsed){
52913             b = this.el.getBox(false, true);
52914         }else{
52915             b = this.collapsedEl.getBox(false, true);
52916         }
52917         return b;
52918     },
52919
52920     getMargins : function(){
52921         return this.collapsed ? this.cmargins : this.margins;
52922     },
52923
52924     highlight : function(){
52925         this.el.addClass("x-layout-panel-dragover");
52926     },
52927
52928     unhighlight : function(){
52929         this.el.removeClass("x-layout-panel-dragover");
52930     },
52931
52932     updateBox : function(box){
52933         this.box = box;
52934         if(!this.collapsed){
52935             this.el.dom.style.left = box.x + "px";
52936             this.el.dom.style.top = box.y + "px";
52937             this.updateBody(box.width, box.height);
52938         }else{
52939             this.collapsedEl.dom.style.left = box.x + "px";
52940             this.collapsedEl.dom.style.top = box.y + "px";
52941             this.collapsedEl.setSize(box.width, box.height);
52942         }
52943         if(this.tabs){
52944             this.tabs.autoSizeTabs();
52945         }
52946     },
52947
52948     updateBody : function(w, h){
52949         if(w !== null){
52950             this.el.setWidth(w);
52951             w -= this.el.getBorderWidth("rl");
52952             if(this.config.adjustments){
52953                 w += this.config.adjustments[0];
52954             }
52955         }
52956         if(h !== null){
52957             this.el.setHeight(h);
52958             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52959             h -= this.el.getBorderWidth("tb");
52960             if(this.config.adjustments){
52961                 h += this.config.adjustments[1];
52962             }
52963             this.bodyEl.setHeight(h);
52964             if(this.tabs){
52965                 h = this.tabs.syncHeight(h);
52966             }
52967         }
52968         if(this.panelSize){
52969             w = w !== null ? w : this.panelSize.width;
52970             h = h !== null ? h : this.panelSize.height;
52971         }
52972         if(this.activePanel){
52973             var el = this.activePanel.getEl();
52974             w = w !== null ? w : el.getWidth();
52975             h = h !== null ? h : el.getHeight();
52976             this.panelSize = {width: w, height: h};
52977             this.activePanel.setSize(w, h);
52978         }
52979         if(Roo.isIE && this.tabs){
52980             this.tabs.el.repaint();
52981         }
52982     },
52983
52984     /**
52985      * Returns the container element for this region.
52986      * @return {Roo.Element}
52987      */
52988     getEl : function(){
52989         return this.el;
52990     },
52991
52992     /**
52993      * Hides this region.
52994      */
52995     hide : function(){
52996         if(!this.collapsed){
52997             this.el.dom.style.left = "-2000px";
52998             this.el.hide();
52999         }else{
53000             this.collapsedEl.dom.style.left = "-2000px";
53001             this.collapsedEl.hide();
53002         }
53003         this.visible = false;
53004         this.fireEvent("visibilitychange", this, false);
53005     },
53006
53007     /**
53008      * Shows this region if it was previously hidden.
53009      */
53010     show : function(){
53011         if(!this.collapsed){
53012             this.el.show();
53013         }else{
53014             this.collapsedEl.show();
53015         }
53016         this.visible = true;
53017         this.fireEvent("visibilitychange", this, true);
53018     },
53019
53020     closeClicked : function(){
53021         if(this.activePanel){
53022             this.remove(this.activePanel);
53023         }
53024     },
53025
53026     collapseClick : function(e){
53027         if(this.isSlid){
53028            e.stopPropagation();
53029            this.slideIn();
53030         }else{
53031            e.stopPropagation();
53032            this.slideOut();
53033         }
53034     },
53035
53036     /**
53037      * Collapses this region.
53038      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53039      */
53040     collapse : function(skipAnim, skipCheck){
53041         if(this.collapsed) {
53042             return;
53043         }
53044         
53045         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53046             
53047             this.collapsed = true;
53048             if(this.split){
53049                 this.split.el.hide();
53050             }
53051             if(this.config.animate && skipAnim !== true){
53052                 this.fireEvent("invalidated", this);
53053                 this.animateCollapse();
53054             }else{
53055                 this.el.setLocation(-20000,-20000);
53056                 this.el.hide();
53057                 this.collapsedEl.show();
53058                 this.fireEvent("collapsed", this);
53059                 this.fireEvent("invalidated", this);
53060             }
53061         }
53062         
53063     },
53064
53065     animateCollapse : function(){
53066         // overridden
53067     },
53068
53069     /**
53070      * Expands this region if it was previously collapsed.
53071      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53072      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53073      */
53074     expand : function(e, skipAnim){
53075         if(e) {
53076             e.stopPropagation();
53077         }
53078         if(!this.collapsed || this.el.hasActiveFx()) {
53079             return;
53080         }
53081         if(this.isSlid){
53082             this.afterSlideIn();
53083             skipAnim = true;
53084         }
53085         this.collapsed = false;
53086         if(this.config.animate && skipAnim !== true){
53087             this.animateExpand();
53088         }else{
53089             this.el.show();
53090             if(this.split){
53091                 this.split.el.show();
53092             }
53093             this.collapsedEl.setLocation(-2000,-2000);
53094             this.collapsedEl.hide();
53095             this.fireEvent("invalidated", this);
53096             this.fireEvent("expanded", this);
53097         }
53098     },
53099
53100     animateExpand : function(){
53101         // overridden
53102     },
53103
53104     initTabs : function()
53105     {
53106         this.bodyEl.setStyle("overflow", "hidden");
53107         var ts = new Roo.TabPanel(
53108                 this.bodyEl.dom,
53109                 {
53110                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53111                     disableTooltips: this.config.disableTabTips,
53112                     toolbar : this.config.toolbar
53113                 }
53114         );
53115         if(this.config.hideTabs){
53116             ts.stripWrap.setDisplayed(false);
53117         }
53118         this.tabs = ts;
53119         ts.resizeTabs = this.config.resizeTabs === true;
53120         ts.minTabWidth = this.config.minTabWidth || 40;
53121         ts.maxTabWidth = this.config.maxTabWidth || 250;
53122         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53123         ts.monitorResize = false;
53124         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53125         ts.bodyEl.addClass('x-layout-tabs-body');
53126         this.panels.each(this.initPanelAsTab, this);
53127     },
53128
53129     initPanelAsTab : function(panel){
53130         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53131                     this.config.closeOnTab && panel.isClosable());
53132         if(panel.tabTip !== undefined){
53133             ti.setTooltip(panel.tabTip);
53134         }
53135         ti.on("activate", function(){
53136               this.setActivePanel(panel);
53137         }, this);
53138         if(this.config.closeOnTab){
53139             ti.on("beforeclose", function(t, e){
53140                 e.cancel = true;
53141                 this.remove(panel);
53142             }, this);
53143         }
53144         return ti;
53145     },
53146
53147     updatePanelTitle : function(panel, title){
53148         if(this.activePanel == panel){
53149             this.updateTitle(title);
53150         }
53151         if(this.tabs){
53152             var ti = this.tabs.getTab(panel.getEl().id);
53153             ti.setText(title);
53154             if(panel.tabTip !== undefined){
53155                 ti.setTooltip(panel.tabTip);
53156             }
53157         }
53158     },
53159
53160     updateTitle : function(title){
53161         if(this.titleTextEl && !this.config.title){
53162             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53163         }
53164     },
53165
53166     setActivePanel : function(panel){
53167         panel = this.getPanel(panel);
53168         if(this.activePanel && this.activePanel != panel){
53169             this.activePanel.setActiveState(false);
53170         }
53171         this.activePanel = panel;
53172         panel.setActiveState(true);
53173         if(this.panelSize){
53174             panel.setSize(this.panelSize.width, this.panelSize.height);
53175         }
53176         if(this.closeBtn){
53177             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53178         }
53179         this.updateTitle(panel.getTitle());
53180         if(this.tabs){
53181             this.fireEvent("invalidated", this);
53182         }
53183         this.fireEvent("panelactivated", this, panel);
53184     },
53185
53186     /**
53187      * Shows the specified panel.
53188      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53189      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53190      */
53191     showPanel : function(panel)
53192     {
53193         panel = this.getPanel(panel);
53194         if(panel){
53195             if(this.tabs){
53196                 var tab = this.tabs.getTab(panel.getEl().id);
53197                 if(tab.isHidden()){
53198                     this.tabs.unhideTab(tab.id);
53199                 }
53200                 tab.activate();
53201             }else{
53202                 this.setActivePanel(panel);
53203             }
53204         }
53205         return panel;
53206     },
53207
53208     /**
53209      * Get the active panel for this region.
53210      * @return {Roo.ContentPanel} The active panel or null
53211      */
53212     getActivePanel : function(){
53213         return this.activePanel;
53214     },
53215
53216     validateVisibility : function(){
53217         if(this.panels.getCount() < 1){
53218             this.updateTitle("&#160;");
53219             this.closeBtn.hide();
53220             this.hide();
53221         }else{
53222             if(!this.isVisible()){
53223                 this.show();
53224             }
53225         }
53226     },
53227
53228     /**
53229      * Adds the passed ContentPanel(s) to this region.
53230      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53231      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53232      */
53233     add : function(panel){
53234         if(arguments.length > 1){
53235             for(var i = 0, len = arguments.length; i < len; i++) {
53236                 this.add(arguments[i]);
53237             }
53238             return null;
53239         }
53240         if(this.hasPanel(panel)){
53241             this.showPanel(panel);
53242             return panel;
53243         }
53244         panel.setRegion(this);
53245         this.panels.add(panel);
53246         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53247             this.bodyEl.dom.appendChild(panel.getEl().dom);
53248             if(panel.background !== true){
53249                 this.setActivePanel(panel);
53250             }
53251             this.fireEvent("paneladded", this, panel);
53252             return panel;
53253         }
53254         if(!this.tabs){
53255             this.initTabs();
53256         }else{
53257             this.initPanelAsTab(panel);
53258         }
53259         if(panel.background !== true){
53260             this.tabs.activate(panel.getEl().id);
53261         }
53262         this.fireEvent("paneladded", this, panel);
53263         return panel;
53264     },
53265
53266     /**
53267      * Hides the tab for the specified panel.
53268      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53269      */
53270     hidePanel : function(panel){
53271         if(this.tabs && (panel = this.getPanel(panel))){
53272             this.tabs.hideTab(panel.getEl().id);
53273         }
53274     },
53275
53276     /**
53277      * Unhides the tab for a previously hidden panel.
53278      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53279      */
53280     unhidePanel : function(panel){
53281         if(this.tabs && (panel = this.getPanel(panel))){
53282             this.tabs.unhideTab(panel.getEl().id);
53283         }
53284     },
53285
53286     clearPanels : function(){
53287         while(this.panels.getCount() > 0){
53288              this.remove(this.panels.first());
53289         }
53290     },
53291
53292     /**
53293      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53294      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53295      * @param {Boolean} preservePanel Overrides the config preservePanel option
53296      * @return {Roo.ContentPanel} The panel that was removed
53297      */
53298     remove : function(panel, preservePanel){
53299         panel = this.getPanel(panel);
53300         if(!panel){
53301             return null;
53302         }
53303         var e = {};
53304         this.fireEvent("beforeremove", this, panel, e);
53305         if(e.cancel === true){
53306             return null;
53307         }
53308         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53309         var panelId = panel.getId();
53310         this.panels.removeKey(panelId);
53311         if(preservePanel){
53312             document.body.appendChild(panel.getEl().dom);
53313         }
53314         if(this.tabs){
53315             this.tabs.removeTab(panel.getEl().id);
53316         }else if (!preservePanel){
53317             this.bodyEl.dom.removeChild(panel.getEl().dom);
53318         }
53319         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53320             var p = this.panels.first();
53321             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53322             tempEl.appendChild(p.getEl().dom);
53323             this.bodyEl.update("");
53324             this.bodyEl.dom.appendChild(p.getEl().dom);
53325             tempEl = null;
53326             this.updateTitle(p.getTitle());
53327             this.tabs = null;
53328             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53329             this.setActivePanel(p);
53330         }
53331         panel.setRegion(null);
53332         if(this.activePanel == panel){
53333             this.activePanel = null;
53334         }
53335         if(this.config.autoDestroy !== false && preservePanel !== true){
53336             try{panel.destroy();}catch(e){}
53337         }
53338         this.fireEvent("panelremoved", this, panel);
53339         return panel;
53340     },
53341
53342     /**
53343      * Returns the TabPanel component used by this region
53344      * @return {Roo.TabPanel}
53345      */
53346     getTabs : function(){
53347         return this.tabs;
53348     },
53349
53350     createTool : function(parentEl, className){
53351         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53352             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53353         btn.addClassOnOver("x-layout-tools-button-over");
53354         return btn;
53355     }
53356 });/*
53357  * Based on:
53358  * Ext JS Library 1.1.1
53359  * Copyright(c) 2006-2007, Ext JS, LLC.
53360  *
53361  * Originally Released Under LGPL - original licence link has changed is not relivant.
53362  *
53363  * Fork - LGPL
53364  * <script type="text/javascript">
53365  */
53366  
53367
53368
53369 /**
53370  * @class Roo.SplitLayoutRegion
53371  * @extends Roo.LayoutRegion
53372  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53373  */
53374 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53375     this.cursor = cursor;
53376     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53377 };
53378
53379 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53380     splitTip : "Drag to resize.",
53381     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53382     useSplitTips : false,
53383
53384     applyConfig : function(config){
53385         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53386         if(config.split){
53387             if(!this.split){
53388                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53389                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53390                 /** The SplitBar for this region 
53391                 * @type Roo.SplitBar */
53392                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53393                 this.split.on("moved", this.onSplitMove, this);
53394                 this.split.useShim = config.useShim === true;
53395                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53396                 if(this.useSplitTips){
53397                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53398                 }
53399                 if(config.collapsible){
53400                     this.split.el.on("dblclick", this.collapse,  this);
53401                 }
53402             }
53403             if(typeof config.minSize != "undefined"){
53404                 this.split.minSize = config.minSize;
53405             }
53406             if(typeof config.maxSize != "undefined"){
53407                 this.split.maxSize = config.maxSize;
53408             }
53409             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53410                 this.hideSplitter();
53411             }
53412         }
53413     },
53414
53415     getHMaxSize : function(){
53416          var cmax = this.config.maxSize || 10000;
53417          var center = this.mgr.getRegion("center");
53418          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53419     },
53420
53421     getVMaxSize : function(){
53422          var cmax = this.config.maxSize || 10000;
53423          var center = this.mgr.getRegion("center");
53424          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53425     },
53426
53427     onSplitMove : function(split, newSize){
53428         this.fireEvent("resized", this, newSize);
53429     },
53430     
53431     /** 
53432      * Returns the {@link Roo.SplitBar} for this region.
53433      * @return {Roo.SplitBar}
53434      */
53435     getSplitBar : function(){
53436         return this.split;
53437     },
53438     
53439     hide : function(){
53440         this.hideSplitter();
53441         Roo.SplitLayoutRegion.superclass.hide.call(this);
53442     },
53443
53444     hideSplitter : function(){
53445         if(this.split){
53446             this.split.el.setLocation(-2000,-2000);
53447             this.split.el.hide();
53448         }
53449     },
53450
53451     show : function(){
53452         if(this.split){
53453             this.split.el.show();
53454         }
53455         Roo.SplitLayoutRegion.superclass.show.call(this);
53456     },
53457     
53458     beforeSlide: function(){
53459         if(Roo.isGecko){// firefox overflow auto bug workaround
53460             this.bodyEl.clip();
53461             if(this.tabs) {
53462                 this.tabs.bodyEl.clip();
53463             }
53464             if(this.activePanel){
53465                 this.activePanel.getEl().clip();
53466                 
53467                 if(this.activePanel.beforeSlide){
53468                     this.activePanel.beforeSlide();
53469                 }
53470             }
53471         }
53472     },
53473     
53474     afterSlide : function(){
53475         if(Roo.isGecko){// firefox overflow auto bug workaround
53476             this.bodyEl.unclip();
53477             if(this.tabs) {
53478                 this.tabs.bodyEl.unclip();
53479             }
53480             if(this.activePanel){
53481                 this.activePanel.getEl().unclip();
53482                 if(this.activePanel.afterSlide){
53483                     this.activePanel.afterSlide();
53484                 }
53485             }
53486         }
53487     },
53488
53489     initAutoHide : function(){
53490         if(this.autoHide !== false){
53491             if(!this.autoHideHd){
53492                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53493                 this.autoHideHd = {
53494                     "mouseout": function(e){
53495                         if(!e.within(this.el, true)){
53496                             st.delay(500);
53497                         }
53498                     },
53499                     "mouseover" : function(e){
53500                         st.cancel();
53501                     },
53502                     scope : this
53503                 };
53504             }
53505             this.el.on(this.autoHideHd);
53506         }
53507     },
53508
53509     clearAutoHide : function(){
53510         if(this.autoHide !== false){
53511             this.el.un("mouseout", this.autoHideHd.mouseout);
53512             this.el.un("mouseover", this.autoHideHd.mouseover);
53513         }
53514     },
53515
53516     clearMonitor : function(){
53517         Roo.get(document).un("click", this.slideInIf, this);
53518     },
53519
53520     // these names are backwards but not changed for compat
53521     slideOut : function(){
53522         if(this.isSlid || this.el.hasActiveFx()){
53523             return;
53524         }
53525         this.isSlid = true;
53526         if(this.collapseBtn){
53527             this.collapseBtn.hide();
53528         }
53529         this.closeBtnState = this.closeBtn.getStyle('display');
53530         this.closeBtn.hide();
53531         if(this.stickBtn){
53532             this.stickBtn.show();
53533         }
53534         this.el.show();
53535         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53536         this.beforeSlide();
53537         this.el.setStyle("z-index", 10001);
53538         this.el.slideIn(this.getSlideAnchor(), {
53539             callback: function(){
53540                 this.afterSlide();
53541                 this.initAutoHide();
53542                 Roo.get(document).on("click", this.slideInIf, this);
53543                 this.fireEvent("slideshow", this);
53544             },
53545             scope: this,
53546             block: true
53547         });
53548     },
53549
53550     afterSlideIn : function(){
53551         this.clearAutoHide();
53552         this.isSlid = false;
53553         this.clearMonitor();
53554         this.el.setStyle("z-index", "");
53555         if(this.collapseBtn){
53556             this.collapseBtn.show();
53557         }
53558         this.closeBtn.setStyle('display', this.closeBtnState);
53559         if(this.stickBtn){
53560             this.stickBtn.hide();
53561         }
53562         this.fireEvent("slidehide", this);
53563     },
53564
53565     slideIn : function(cb){
53566         if(!this.isSlid || this.el.hasActiveFx()){
53567             Roo.callback(cb);
53568             return;
53569         }
53570         this.isSlid = false;
53571         this.beforeSlide();
53572         this.el.slideOut(this.getSlideAnchor(), {
53573             callback: function(){
53574                 this.el.setLeftTop(-10000, -10000);
53575                 this.afterSlide();
53576                 this.afterSlideIn();
53577                 Roo.callback(cb);
53578             },
53579             scope: this,
53580             block: true
53581         });
53582     },
53583     
53584     slideInIf : function(e){
53585         if(!e.within(this.el)){
53586             this.slideIn();
53587         }
53588     },
53589
53590     animateCollapse : function(){
53591         this.beforeSlide();
53592         this.el.setStyle("z-index", 20000);
53593         var anchor = this.getSlideAnchor();
53594         this.el.slideOut(anchor, {
53595             callback : function(){
53596                 this.el.setStyle("z-index", "");
53597                 this.collapsedEl.slideIn(anchor, {duration:.3});
53598                 this.afterSlide();
53599                 this.el.setLocation(-10000,-10000);
53600                 this.el.hide();
53601                 this.fireEvent("collapsed", this);
53602             },
53603             scope: this,
53604             block: true
53605         });
53606     },
53607
53608     animateExpand : function(){
53609         this.beforeSlide();
53610         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53611         this.el.setStyle("z-index", 20000);
53612         this.collapsedEl.hide({
53613             duration:.1
53614         });
53615         this.el.slideIn(this.getSlideAnchor(), {
53616             callback : function(){
53617                 this.el.setStyle("z-index", "");
53618                 this.afterSlide();
53619                 if(this.split){
53620                     this.split.el.show();
53621                 }
53622                 this.fireEvent("invalidated", this);
53623                 this.fireEvent("expanded", this);
53624             },
53625             scope: this,
53626             block: true
53627         });
53628     },
53629
53630     anchors : {
53631         "west" : "left",
53632         "east" : "right",
53633         "north" : "top",
53634         "south" : "bottom"
53635     },
53636
53637     sanchors : {
53638         "west" : "l",
53639         "east" : "r",
53640         "north" : "t",
53641         "south" : "b"
53642     },
53643
53644     canchors : {
53645         "west" : "tl-tr",
53646         "east" : "tr-tl",
53647         "north" : "tl-bl",
53648         "south" : "bl-tl"
53649     },
53650
53651     getAnchor : function(){
53652         return this.anchors[this.position];
53653     },
53654
53655     getCollapseAnchor : function(){
53656         return this.canchors[this.position];
53657     },
53658
53659     getSlideAnchor : function(){
53660         return this.sanchors[this.position];
53661     },
53662
53663     getAlignAdj : function(){
53664         var cm = this.cmargins;
53665         switch(this.position){
53666             case "west":
53667                 return [0, 0];
53668             break;
53669             case "east":
53670                 return [0, 0];
53671             break;
53672             case "north":
53673                 return [0, 0];
53674             break;
53675             case "south":
53676                 return [0, 0];
53677             break;
53678         }
53679     },
53680
53681     getExpandAdj : function(){
53682         var c = this.collapsedEl, cm = this.cmargins;
53683         switch(this.position){
53684             case "west":
53685                 return [-(cm.right+c.getWidth()+cm.left), 0];
53686             break;
53687             case "east":
53688                 return [cm.right+c.getWidth()+cm.left, 0];
53689             break;
53690             case "north":
53691                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53692             break;
53693             case "south":
53694                 return [0, cm.top+cm.bottom+c.getHeight()];
53695             break;
53696         }
53697     }
53698 });/*
53699  * Based on:
53700  * Ext JS Library 1.1.1
53701  * Copyright(c) 2006-2007, Ext JS, LLC.
53702  *
53703  * Originally Released Under LGPL - original licence link has changed is not relivant.
53704  *
53705  * Fork - LGPL
53706  * <script type="text/javascript">
53707  */
53708 /*
53709  * These classes are private internal classes
53710  */
53711 Roo.CenterLayoutRegion = function(mgr, config){
53712     Roo.LayoutRegion.call(this, mgr, config, "center");
53713     this.visible = true;
53714     this.minWidth = config.minWidth || 20;
53715     this.minHeight = config.minHeight || 20;
53716 };
53717
53718 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53719     hide : function(){
53720         // center panel can't be hidden
53721     },
53722     
53723     show : function(){
53724         // center panel can't be hidden
53725     },
53726     
53727     getMinWidth: function(){
53728         return this.minWidth;
53729     },
53730     
53731     getMinHeight: function(){
53732         return this.minHeight;
53733     }
53734 });
53735
53736
53737 Roo.NorthLayoutRegion = function(mgr, config){
53738     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53739     if(this.split){
53740         this.split.placement = Roo.SplitBar.TOP;
53741         this.split.orientation = Roo.SplitBar.VERTICAL;
53742         this.split.el.addClass("x-layout-split-v");
53743     }
53744     var size = config.initialSize || config.height;
53745     if(typeof size != "undefined"){
53746         this.el.setHeight(size);
53747     }
53748 };
53749 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53750     orientation: Roo.SplitBar.VERTICAL,
53751     getBox : function(){
53752         if(this.collapsed){
53753             return this.collapsedEl.getBox();
53754         }
53755         var box = this.el.getBox();
53756         if(this.split){
53757             box.height += this.split.el.getHeight();
53758         }
53759         return box;
53760     },
53761     
53762     updateBox : function(box){
53763         if(this.split && !this.collapsed){
53764             box.height -= this.split.el.getHeight();
53765             this.split.el.setLeft(box.x);
53766             this.split.el.setTop(box.y+box.height);
53767             this.split.el.setWidth(box.width);
53768         }
53769         if(this.collapsed){
53770             this.updateBody(box.width, null);
53771         }
53772         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53773     }
53774 });
53775
53776 Roo.SouthLayoutRegion = function(mgr, config){
53777     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53778     if(this.split){
53779         this.split.placement = Roo.SplitBar.BOTTOM;
53780         this.split.orientation = Roo.SplitBar.VERTICAL;
53781         this.split.el.addClass("x-layout-split-v");
53782     }
53783     var size = config.initialSize || config.height;
53784     if(typeof size != "undefined"){
53785         this.el.setHeight(size);
53786     }
53787 };
53788 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53789     orientation: Roo.SplitBar.VERTICAL,
53790     getBox : function(){
53791         if(this.collapsed){
53792             return this.collapsedEl.getBox();
53793         }
53794         var box = this.el.getBox();
53795         if(this.split){
53796             var sh = this.split.el.getHeight();
53797             box.height += sh;
53798             box.y -= sh;
53799         }
53800         return box;
53801     },
53802     
53803     updateBox : function(box){
53804         if(this.split && !this.collapsed){
53805             var sh = this.split.el.getHeight();
53806             box.height -= sh;
53807             box.y += sh;
53808             this.split.el.setLeft(box.x);
53809             this.split.el.setTop(box.y-sh);
53810             this.split.el.setWidth(box.width);
53811         }
53812         if(this.collapsed){
53813             this.updateBody(box.width, null);
53814         }
53815         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53816     }
53817 });
53818
53819 Roo.EastLayoutRegion = function(mgr, config){
53820     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53821     if(this.split){
53822         this.split.placement = Roo.SplitBar.RIGHT;
53823         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53824         this.split.el.addClass("x-layout-split-h");
53825     }
53826     var size = config.initialSize || config.width;
53827     if(typeof size != "undefined"){
53828         this.el.setWidth(size);
53829     }
53830 };
53831 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53832     orientation: Roo.SplitBar.HORIZONTAL,
53833     getBox : function(){
53834         if(this.collapsed){
53835             return this.collapsedEl.getBox();
53836         }
53837         var box = this.el.getBox();
53838         if(this.split){
53839             var sw = this.split.el.getWidth();
53840             box.width += sw;
53841             box.x -= sw;
53842         }
53843         return box;
53844     },
53845
53846     updateBox : function(box){
53847         if(this.split && !this.collapsed){
53848             var sw = this.split.el.getWidth();
53849             box.width -= sw;
53850             this.split.el.setLeft(box.x);
53851             this.split.el.setTop(box.y);
53852             this.split.el.setHeight(box.height);
53853             box.x += sw;
53854         }
53855         if(this.collapsed){
53856             this.updateBody(null, box.height);
53857         }
53858         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53859     }
53860 });
53861
53862 Roo.WestLayoutRegion = function(mgr, config){
53863     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53864     if(this.split){
53865         this.split.placement = Roo.SplitBar.LEFT;
53866         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53867         this.split.el.addClass("x-layout-split-h");
53868     }
53869     var size = config.initialSize || config.width;
53870     if(typeof size != "undefined"){
53871         this.el.setWidth(size);
53872     }
53873 };
53874 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53875     orientation: Roo.SplitBar.HORIZONTAL,
53876     getBox : function(){
53877         if(this.collapsed){
53878             return this.collapsedEl.getBox();
53879         }
53880         var box = this.el.getBox();
53881         if(this.split){
53882             box.width += this.split.el.getWidth();
53883         }
53884         return box;
53885     },
53886     
53887     updateBox : function(box){
53888         if(this.split && !this.collapsed){
53889             var sw = this.split.el.getWidth();
53890             box.width -= sw;
53891             this.split.el.setLeft(box.x+box.width);
53892             this.split.el.setTop(box.y);
53893             this.split.el.setHeight(box.height);
53894         }
53895         if(this.collapsed){
53896             this.updateBody(null, box.height);
53897         }
53898         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53899     }
53900 });
53901 /*
53902  * Based on:
53903  * Ext JS Library 1.1.1
53904  * Copyright(c) 2006-2007, Ext JS, LLC.
53905  *
53906  * Originally Released Under LGPL - original licence link has changed is not relivant.
53907  *
53908  * Fork - LGPL
53909  * <script type="text/javascript">
53910  */
53911  
53912  
53913 /*
53914  * Private internal class for reading and applying state
53915  */
53916 Roo.LayoutStateManager = function(layout){
53917      // default empty state
53918      this.state = {
53919         north: {},
53920         south: {},
53921         east: {},
53922         west: {}       
53923     };
53924 };
53925
53926 Roo.LayoutStateManager.prototype = {
53927     init : function(layout, provider){
53928         this.provider = provider;
53929         var state = provider.get(layout.id+"-layout-state");
53930         if(state){
53931             var wasUpdating = layout.isUpdating();
53932             if(!wasUpdating){
53933                 layout.beginUpdate();
53934             }
53935             for(var key in state){
53936                 if(typeof state[key] != "function"){
53937                     var rstate = state[key];
53938                     var r = layout.getRegion(key);
53939                     if(r && rstate){
53940                         if(rstate.size){
53941                             r.resizeTo(rstate.size);
53942                         }
53943                         if(rstate.collapsed == true){
53944                             r.collapse(true);
53945                         }else{
53946                             r.expand(null, true);
53947                         }
53948                     }
53949                 }
53950             }
53951             if(!wasUpdating){
53952                 layout.endUpdate();
53953             }
53954             this.state = state; 
53955         }
53956         this.layout = layout;
53957         layout.on("regionresized", this.onRegionResized, this);
53958         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53959         layout.on("regionexpanded", this.onRegionExpanded, this);
53960     },
53961     
53962     storeState : function(){
53963         this.provider.set(this.layout.id+"-layout-state", this.state);
53964     },
53965     
53966     onRegionResized : function(region, newSize){
53967         this.state[region.getPosition()].size = newSize;
53968         this.storeState();
53969     },
53970     
53971     onRegionCollapsed : function(region){
53972         this.state[region.getPosition()].collapsed = true;
53973         this.storeState();
53974     },
53975     
53976     onRegionExpanded : function(region){
53977         this.state[region.getPosition()].collapsed = false;
53978         this.storeState();
53979     }
53980 };/*
53981  * Based on:
53982  * Ext JS Library 1.1.1
53983  * Copyright(c) 2006-2007, Ext JS, LLC.
53984  *
53985  * Originally Released Under LGPL - original licence link has changed is not relivant.
53986  *
53987  * Fork - LGPL
53988  * <script type="text/javascript">
53989  */
53990 /**
53991  * @class Roo.ContentPanel
53992  * @extends Roo.util.Observable
53993  * A basic ContentPanel element.
53994  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53995  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53996  * @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
53997  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53998  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53999  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54000  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54001  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54002  * @cfg {String} title          The title for this panel
54003  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54004  * @cfg {String} url            Calls {@link #setUrl} with this value
54005  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54006  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54007  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54008  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54009
54010  * @constructor
54011  * Create a new ContentPanel.
54012  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54013  * @param {String/Object} config A string to set only the title or a config object
54014  * @param {String} content (optional) Set the HTML content for this panel
54015  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54016  */
54017 Roo.ContentPanel = function(el, config, content){
54018     
54019      
54020     /*
54021     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54022         config = el;
54023         el = Roo.id();
54024     }
54025     if (config && config.parentLayout) { 
54026         el = config.parentLayout.el.createChild(); 
54027     }
54028     */
54029     if(el.autoCreate){ // xtype is available if this is called from factory
54030         config = el;
54031         el = Roo.id();
54032     }
54033     this.el = Roo.get(el);
54034     if(!this.el && config && config.autoCreate){
54035         if(typeof config.autoCreate == "object"){
54036             if(!config.autoCreate.id){
54037                 config.autoCreate.id = config.id||el;
54038             }
54039             this.el = Roo.DomHelper.append(document.body,
54040                         config.autoCreate, true);
54041         }else{
54042             this.el = Roo.DomHelper.append(document.body,
54043                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54044         }
54045     }
54046     this.closable = false;
54047     this.loaded = false;
54048     this.active = false;
54049     if(typeof config == "string"){
54050         this.title = config;
54051     }else{
54052         Roo.apply(this, config);
54053     }
54054     
54055     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54056         this.wrapEl = this.el.wrap();
54057         this.toolbar.container = this.el.insertSibling(false, 'before');
54058         this.toolbar = new Roo.Toolbar(this.toolbar);
54059     }
54060     
54061     // xtype created footer. - not sure if will work as we normally have to render first..
54062     if (this.footer && !this.footer.el && this.footer.xtype) {
54063         if (!this.wrapEl) {
54064             this.wrapEl = this.el.wrap();
54065         }
54066     
54067         this.footer.container = this.wrapEl.createChild();
54068          
54069         this.footer = Roo.factory(this.footer, Roo);
54070         
54071     }
54072     
54073     if(this.resizeEl){
54074         this.resizeEl = Roo.get(this.resizeEl, true);
54075     }else{
54076         this.resizeEl = this.el;
54077     }
54078     // handle view.xtype
54079     
54080  
54081     
54082     
54083     this.addEvents({
54084         /**
54085          * @event activate
54086          * Fires when this panel is activated. 
54087          * @param {Roo.ContentPanel} this
54088          */
54089         "activate" : true,
54090         /**
54091          * @event deactivate
54092          * Fires when this panel is activated. 
54093          * @param {Roo.ContentPanel} this
54094          */
54095         "deactivate" : true,
54096
54097         /**
54098          * @event resize
54099          * Fires when this panel is resized if fitToFrame is true.
54100          * @param {Roo.ContentPanel} this
54101          * @param {Number} width The width after any component adjustments
54102          * @param {Number} height The height after any component adjustments
54103          */
54104         "resize" : true,
54105         
54106          /**
54107          * @event render
54108          * Fires when this tab is created
54109          * @param {Roo.ContentPanel} this
54110          */
54111         "render" : true
54112          
54113         
54114     });
54115     
54116
54117     
54118     
54119     if(this.autoScroll){
54120         this.resizeEl.setStyle("overflow", "auto");
54121     } else {
54122         // fix randome scrolling
54123         this.el.on('scroll', function() {
54124             Roo.log('fix random scolling');
54125             this.scrollTo('top',0); 
54126         });
54127     }
54128     content = content || this.content;
54129     if(content){
54130         this.setContent(content);
54131     }
54132     if(config && config.url){
54133         this.setUrl(this.url, this.params, this.loadOnce);
54134     }
54135     
54136     
54137     
54138     Roo.ContentPanel.superclass.constructor.call(this);
54139     
54140     if (this.view && typeof(this.view.xtype) != 'undefined') {
54141         this.view.el = this.el.appendChild(document.createElement("div"));
54142         this.view = Roo.factory(this.view); 
54143         this.view.render  &&  this.view.render(false, '');  
54144     }
54145     
54146     
54147     this.fireEvent('render', this);
54148 };
54149
54150 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54151     tabTip:'',
54152     setRegion : function(region){
54153         this.region = region;
54154         if(region){
54155            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54156         }else{
54157            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54158         } 
54159     },
54160     
54161     /**
54162      * Returns the toolbar for this Panel if one was configured. 
54163      * @return {Roo.Toolbar} 
54164      */
54165     getToolbar : function(){
54166         return this.toolbar;
54167     },
54168     
54169     setActiveState : function(active){
54170         this.active = active;
54171         if(!active){
54172             this.fireEvent("deactivate", this);
54173         }else{
54174             this.fireEvent("activate", this);
54175         }
54176     },
54177     /**
54178      * Updates this panel's element
54179      * @param {String} content The new content
54180      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54181     */
54182     setContent : function(content, loadScripts){
54183         this.el.update(content, loadScripts);
54184     },
54185
54186     ignoreResize : function(w, h){
54187         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54188             return true;
54189         }else{
54190             this.lastSize = {width: w, height: h};
54191             return false;
54192         }
54193     },
54194     /**
54195      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54196      * @return {Roo.UpdateManager} The UpdateManager
54197      */
54198     getUpdateManager : function(){
54199         return this.el.getUpdateManager();
54200     },
54201      /**
54202      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54203      * @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:
54204 <pre><code>
54205 panel.load({
54206     url: "your-url.php",
54207     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54208     callback: yourFunction,
54209     scope: yourObject, //(optional scope)
54210     discardUrl: false,
54211     nocache: false,
54212     text: "Loading...",
54213     timeout: 30,
54214     scripts: false
54215 });
54216 </code></pre>
54217      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54218      * 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.
54219      * @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}
54220      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54221      * @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.
54222      * @return {Roo.ContentPanel} this
54223      */
54224     load : function(){
54225         var um = this.el.getUpdateManager();
54226         um.update.apply(um, arguments);
54227         return this;
54228     },
54229
54230
54231     /**
54232      * 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.
54233      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54234      * @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)
54235      * @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)
54236      * @return {Roo.UpdateManager} The UpdateManager
54237      */
54238     setUrl : function(url, params, loadOnce){
54239         if(this.refreshDelegate){
54240             this.removeListener("activate", this.refreshDelegate);
54241         }
54242         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54243         this.on("activate", this.refreshDelegate);
54244         return this.el.getUpdateManager();
54245     },
54246     
54247     _handleRefresh : function(url, params, loadOnce){
54248         if(!loadOnce || !this.loaded){
54249             var updater = this.el.getUpdateManager();
54250             updater.update(url, params, this._setLoaded.createDelegate(this));
54251         }
54252     },
54253     
54254     _setLoaded : function(){
54255         this.loaded = true;
54256     }, 
54257     
54258     /**
54259      * Returns this panel's id
54260      * @return {String} 
54261      */
54262     getId : function(){
54263         return this.el.id;
54264     },
54265     
54266     /** 
54267      * Returns this panel's element - used by regiosn to add.
54268      * @return {Roo.Element} 
54269      */
54270     getEl : function(){
54271         return this.wrapEl || this.el;
54272     },
54273     
54274     adjustForComponents : function(width, height)
54275     {
54276         //Roo.log('adjustForComponents ');
54277         if(this.resizeEl != this.el){
54278             width -= this.el.getFrameWidth('lr');
54279             height -= this.el.getFrameWidth('tb');
54280         }
54281         if(this.toolbar){
54282             var te = this.toolbar.getEl();
54283             height -= te.getHeight();
54284             te.setWidth(width);
54285         }
54286         if(this.footer){
54287             var te = this.footer.getEl();
54288             //Roo.log("footer:" + te.getHeight());
54289             
54290             height -= te.getHeight();
54291             te.setWidth(width);
54292         }
54293         
54294         
54295         if(this.adjustments){
54296             width += this.adjustments[0];
54297             height += this.adjustments[1];
54298         }
54299         return {"width": width, "height": height};
54300     },
54301     
54302     setSize : function(width, height){
54303         if(this.fitToFrame && !this.ignoreResize(width, height)){
54304             if(this.fitContainer && this.resizeEl != this.el){
54305                 this.el.setSize(width, height);
54306             }
54307             var size = this.adjustForComponents(width, height);
54308             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54309             this.fireEvent('resize', this, size.width, size.height);
54310         }
54311     },
54312     
54313     /**
54314      * Returns this panel's title
54315      * @return {String} 
54316      */
54317     getTitle : function(){
54318         return this.title;
54319     },
54320     
54321     /**
54322      * Set this panel's title
54323      * @param {String} title
54324      */
54325     setTitle : function(title){
54326         this.title = title;
54327         if(this.region){
54328             this.region.updatePanelTitle(this, title);
54329         }
54330     },
54331     
54332     /**
54333      * Returns true is this panel was configured to be closable
54334      * @return {Boolean} 
54335      */
54336     isClosable : function(){
54337         return this.closable;
54338     },
54339     
54340     beforeSlide : function(){
54341         this.el.clip();
54342         this.resizeEl.clip();
54343     },
54344     
54345     afterSlide : function(){
54346         this.el.unclip();
54347         this.resizeEl.unclip();
54348     },
54349     
54350     /**
54351      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54352      *   Will fail silently if the {@link #setUrl} method has not been called.
54353      *   This does not activate the panel, just updates its content.
54354      */
54355     refresh : function(){
54356         if(this.refreshDelegate){
54357            this.loaded = false;
54358            this.refreshDelegate();
54359         }
54360     },
54361     
54362     /**
54363      * Destroys this panel
54364      */
54365     destroy : function(){
54366         this.el.removeAllListeners();
54367         var tempEl = document.createElement("span");
54368         tempEl.appendChild(this.el.dom);
54369         tempEl.innerHTML = "";
54370         this.el.remove();
54371         this.el = null;
54372     },
54373     
54374     /**
54375      * form - if the content panel contains a form - this is a reference to it.
54376      * @type {Roo.form.Form}
54377      */
54378     form : false,
54379     /**
54380      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54381      *    This contains a reference to it.
54382      * @type {Roo.View}
54383      */
54384     view : false,
54385     
54386       /**
54387      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54388      * <pre><code>
54389
54390 layout.addxtype({
54391        xtype : 'Form',
54392        items: [ .... ]
54393    }
54394 );
54395
54396 </code></pre>
54397      * @param {Object} cfg Xtype definition of item to add.
54398      */
54399     
54400     addxtype : function(cfg) {
54401         // add form..
54402         if (cfg.xtype.match(/^Form$/)) {
54403             
54404             var el;
54405             //if (this.footer) {
54406             //    el = this.footer.container.insertSibling(false, 'before');
54407             //} else {
54408                 el = this.el.createChild();
54409             //}
54410
54411             this.form = new  Roo.form.Form(cfg);
54412             
54413             
54414             if ( this.form.allItems.length) {
54415                 this.form.render(el.dom);
54416             }
54417             return this.form;
54418         }
54419         // should only have one of theses..
54420         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54421             // views.. should not be just added - used named prop 'view''
54422             
54423             cfg.el = this.el.appendChild(document.createElement("div"));
54424             // factory?
54425             
54426             var ret = new Roo.factory(cfg);
54427              
54428              ret.render && ret.render(false, ''); // render blank..
54429             this.view = ret;
54430             return ret;
54431         }
54432         return false;
54433     }
54434 });
54435
54436 /**
54437  * @class Roo.GridPanel
54438  * @extends Roo.ContentPanel
54439  * @constructor
54440  * Create a new GridPanel.
54441  * @param {Roo.grid.Grid} grid The grid for this panel
54442  * @param {String/Object} config A string to set only the panel's title, or a config object
54443  */
54444 Roo.GridPanel = function(grid, config){
54445     
54446   
54447     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54448         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54449         
54450     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54451     
54452     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54453     
54454     if(this.toolbar){
54455         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54456     }
54457     // xtype created footer. - not sure if will work as we normally have to render first..
54458     if (this.footer && !this.footer.el && this.footer.xtype) {
54459         
54460         this.footer.container = this.grid.getView().getFooterPanel(true);
54461         this.footer.dataSource = this.grid.dataSource;
54462         this.footer = Roo.factory(this.footer, Roo);
54463         
54464     }
54465     
54466     grid.monitorWindowResize = false; // turn off autosizing
54467     grid.autoHeight = false;
54468     grid.autoWidth = false;
54469     this.grid = grid;
54470     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54471 };
54472
54473 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54474     getId : function(){
54475         return this.grid.id;
54476     },
54477     
54478     /**
54479      * Returns the grid for this panel
54480      * @return {Roo.grid.Grid} 
54481      */
54482     getGrid : function(){
54483         return this.grid;    
54484     },
54485     
54486     setSize : function(width, height){
54487         if(!this.ignoreResize(width, height)){
54488             var grid = this.grid;
54489             var size = this.adjustForComponents(width, height);
54490             grid.getGridEl().setSize(size.width, size.height);
54491             grid.autoSize();
54492         }
54493     },
54494     
54495     beforeSlide : function(){
54496         this.grid.getView().scroller.clip();
54497     },
54498     
54499     afterSlide : function(){
54500         this.grid.getView().scroller.unclip();
54501     },
54502     
54503     destroy : function(){
54504         this.grid.destroy();
54505         delete this.grid;
54506         Roo.GridPanel.superclass.destroy.call(this); 
54507     }
54508 });
54509
54510
54511 /**
54512  * @class Roo.NestedLayoutPanel
54513  * @extends Roo.ContentPanel
54514  * @constructor
54515  * Create a new NestedLayoutPanel.
54516  * 
54517  * 
54518  * @param {Roo.BorderLayout} layout The layout for this panel
54519  * @param {String/Object} config A string to set only the title or a config object
54520  */
54521 Roo.NestedLayoutPanel = function(layout, config)
54522 {
54523     // construct with only one argument..
54524     /* FIXME - implement nicer consturctors
54525     if (layout.layout) {
54526         config = layout;
54527         layout = config.layout;
54528         delete config.layout;
54529     }
54530     if (layout.xtype && !layout.getEl) {
54531         // then layout needs constructing..
54532         layout = Roo.factory(layout, Roo);
54533     }
54534     */
54535     
54536     
54537     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54538     
54539     layout.monitorWindowResize = false; // turn off autosizing
54540     this.layout = layout;
54541     this.layout.getEl().addClass("x-layout-nested-layout");
54542     
54543     
54544     
54545     
54546 };
54547
54548 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54549
54550     setSize : function(width, height){
54551         if(!this.ignoreResize(width, height)){
54552             var size = this.adjustForComponents(width, height);
54553             var el = this.layout.getEl();
54554             el.setSize(size.width, size.height);
54555             var touch = el.dom.offsetWidth;
54556             this.layout.layout();
54557             // ie requires a double layout on the first pass
54558             if(Roo.isIE && !this.initialized){
54559                 this.initialized = true;
54560                 this.layout.layout();
54561             }
54562         }
54563     },
54564     
54565     // activate all subpanels if not currently active..
54566     
54567     setActiveState : function(active){
54568         this.active = active;
54569         if(!active){
54570             this.fireEvent("deactivate", this);
54571             return;
54572         }
54573         
54574         this.fireEvent("activate", this);
54575         // not sure if this should happen before or after..
54576         if (!this.layout) {
54577             return; // should not happen..
54578         }
54579         var reg = false;
54580         for (var r in this.layout.regions) {
54581             reg = this.layout.getRegion(r);
54582             if (reg.getActivePanel()) {
54583                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54584                 reg.setActivePanel(reg.getActivePanel());
54585                 continue;
54586             }
54587             if (!reg.panels.length) {
54588                 continue;
54589             }
54590             reg.showPanel(reg.getPanel(0));
54591         }
54592         
54593         
54594         
54595         
54596     },
54597     
54598     /**
54599      * Returns the nested BorderLayout for this panel
54600      * @return {Roo.BorderLayout} 
54601      */
54602     getLayout : function(){
54603         return this.layout;
54604     },
54605     
54606      /**
54607      * Adds a xtype elements to the layout of the nested panel
54608      * <pre><code>
54609
54610 panel.addxtype({
54611        xtype : 'ContentPanel',
54612        region: 'west',
54613        items: [ .... ]
54614    }
54615 );
54616
54617 panel.addxtype({
54618         xtype : 'NestedLayoutPanel',
54619         region: 'west',
54620         layout: {
54621            center: { },
54622            west: { }   
54623         },
54624         items : [ ... list of content panels or nested layout panels.. ]
54625    }
54626 );
54627 </code></pre>
54628      * @param {Object} cfg Xtype definition of item to add.
54629      */
54630     addxtype : function(cfg) {
54631         return this.layout.addxtype(cfg);
54632     
54633     }
54634 });
54635
54636 Roo.ScrollPanel = function(el, config, content){
54637     config = config || {};
54638     config.fitToFrame = true;
54639     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54640     
54641     this.el.dom.style.overflow = "hidden";
54642     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54643     this.el.removeClass("x-layout-inactive-content");
54644     this.el.on("mousewheel", this.onWheel, this);
54645
54646     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54647     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54648     up.unselectable(); down.unselectable();
54649     up.on("click", this.scrollUp, this);
54650     down.on("click", this.scrollDown, this);
54651     up.addClassOnOver("x-scroller-btn-over");
54652     down.addClassOnOver("x-scroller-btn-over");
54653     up.addClassOnClick("x-scroller-btn-click");
54654     down.addClassOnClick("x-scroller-btn-click");
54655     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54656
54657     this.resizeEl = this.el;
54658     this.el = wrap; this.up = up; this.down = down;
54659 };
54660
54661 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54662     increment : 100,
54663     wheelIncrement : 5,
54664     scrollUp : function(){
54665         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54666     },
54667
54668     scrollDown : function(){
54669         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54670     },
54671
54672     afterScroll : function(){
54673         var el = this.resizeEl;
54674         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54675         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54676         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54677     },
54678
54679     setSize : function(){
54680         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54681         this.afterScroll();
54682     },
54683
54684     onWheel : function(e){
54685         var d = e.getWheelDelta();
54686         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54687         this.afterScroll();
54688         e.stopEvent();
54689     },
54690
54691     setContent : function(content, loadScripts){
54692         this.resizeEl.update(content, loadScripts);
54693     }
54694
54695 });
54696
54697
54698
54699
54700
54701
54702
54703
54704
54705 /**
54706  * @class Roo.TreePanel
54707  * @extends Roo.ContentPanel
54708  * @constructor
54709  * Create a new TreePanel. - defaults to fit/scoll contents.
54710  * @param {String/Object} config A string to set only the panel's title, or a config object
54711  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54712  */
54713 Roo.TreePanel = function(config){
54714     var el = config.el;
54715     var tree = config.tree;
54716     delete config.tree; 
54717     delete config.el; // hopefull!
54718     
54719     // wrapper for IE7 strict & safari scroll issue
54720     
54721     var treeEl = el.createChild();
54722     config.resizeEl = treeEl;
54723     
54724     
54725     
54726     Roo.TreePanel.superclass.constructor.call(this, el, config);
54727  
54728  
54729     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54730     //console.log(tree);
54731     this.on('activate', function()
54732     {
54733         if (this.tree.rendered) {
54734             return;
54735         }
54736         //console.log('render tree');
54737         this.tree.render();
54738     });
54739     // this should not be needed.. - it's actually the 'el' that resizes?
54740     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54741     
54742     //this.on('resize',  function (cp, w, h) {
54743     //        this.tree.innerCt.setWidth(w);
54744     //        this.tree.innerCt.setHeight(h);
54745     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54746     //});
54747
54748         
54749     
54750 };
54751
54752 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54753     fitToFrame : true,
54754     autoScroll : true
54755 });
54756
54757
54758
54759
54760
54761
54762
54763
54764
54765
54766
54767 /*
54768  * Based on:
54769  * Ext JS Library 1.1.1
54770  * Copyright(c) 2006-2007, Ext JS, LLC.
54771  *
54772  * Originally Released Under LGPL - original licence link has changed is not relivant.
54773  *
54774  * Fork - LGPL
54775  * <script type="text/javascript">
54776  */
54777  
54778
54779 /**
54780  * @class Roo.ReaderLayout
54781  * @extends Roo.BorderLayout
54782  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54783  * center region containing two nested regions (a top one for a list view and one for item preview below),
54784  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54785  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54786  * expedites the setup of the overall layout and regions for this common application style.
54787  * Example:
54788  <pre><code>
54789 var reader = new Roo.ReaderLayout();
54790 var CP = Roo.ContentPanel;  // shortcut for adding
54791
54792 reader.beginUpdate();
54793 reader.add("north", new CP("north", "North"));
54794 reader.add("west", new CP("west", {title: "West"}));
54795 reader.add("east", new CP("east", {title: "East"}));
54796
54797 reader.regions.listView.add(new CP("listView", "List"));
54798 reader.regions.preview.add(new CP("preview", "Preview"));
54799 reader.endUpdate();
54800 </code></pre>
54801 * @constructor
54802 * Create a new ReaderLayout
54803 * @param {Object} config Configuration options
54804 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54805 * document.body if omitted)
54806 */
54807 Roo.ReaderLayout = function(config, renderTo){
54808     var c = config || {size:{}};
54809     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54810         north: c.north !== false ? Roo.apply({
54811             split:false,
54812             initialSize: 32,
54813             titlebar: false
54814         }, c.north) : false,
54815         west: c.west !== false ? Roo.apply({
54816             split:true,
54817             initialSize: 200,
54818             minSize: 175,
54819             maxSize: 400,
54820             titlebar: true,
54821             collapsible: true,
54822             animate: true,
54823             margins:{left:5,right:0,bottom:5,top:5},
54824             cmargins:{left:5,right:5,bottom:5,top:5}
54825         }, c.west) : false,
54826         east: c.east !== false ? Roo.apply({
54827             split:true,
54828             initialSize: 200,
54829             minSize: 175,
54830             maxSize: 400,
54831             titlebar: true,
54832             collapsible: true,
54833             animate: true,
54834             margins:{left:0,right:5,bottom:5,top:5},
54835             cmargins:{left:5,right:5,bottom:5,top:5}
54836         }, c.east) : false,
54837         center: Roo.apply({
54838             tabPosition: 'top',
54839             autoScroll:false,
54840             closeOnTab: true,
54841             titlebar:false,
54842             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54843         }, c.center)
54844     });
54845
54846     this.el.addClass('x-reader');
54847
54848     this.beginUpdate();
54849
54850     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54851         south: c.preview !== false ? Roo.apply({
54852             split:true,
54853             initialSize: 200,
54854             minSize: 100,
54855             autoScroll:true,
54856             collapsible:true,
54857             titlebar: true,
54858             cmargins:{top:5,left:0, right:0, bottom:0}
54859         }, c.preview) : false,
54860         center: Roo.apply({
54861             autoScroll:false,
54862             titlebar:false,
54863             minHeight:200
54864         }, c.listView)
54865     });
54866     this.add('center', new Roo.NestedLayoutPanel(inner,
54867             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54868
54869     this.endUpdate();
54870
54871     this.regions.preview = inner.getRegion('south');
54872     this.regions.listView = inner.getRegion('center');
54873 };
54874
54875 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54876  * Based on:
54877  * Ext JS Library 1.1.1
54878  * Copyright(c) 2006-2007, Ext JS, LLC.
54879  *
54880  * Originally Released Under LGPL - original licence link has changed is not relivant.
54881  *
54882  * Fork - LGPL
54883  * <script type="text/javascript">
54884  */
54885  
54886 /**
54887  * @class Roo.grid.Grid
54888  * @extends Roo.util.Observable
54889  * This class represents the primary interface of a component based grid control.
54890  * <br><br>Usage:<pre><code>
54891  var grid = new Roo.grid.Grid("my-container-id", {
54892      ds: myDataStore,
54893      cm: myColModel,
54894      selModel: mySelectionModel,
54895      autoSizeColumns: true,
54896      monitorWindowResize: false,
54897      trackMouseOver: true
54898  });
54899  // set any options
54900  grid.render();
54901  * </code></pre>
54902  * <b>Common Problems:</b><br/>
54903  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54904  * element will correct this<br/>
54905  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54906  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54907  * are unpredictable.<br/>
54908  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54909  * grid to calculate dimensions/offsets.<br/>
54910   * @constructor
54911  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54912  * The container MUST have some type of size defined for the grid to fill. The container will be
54913  * automatically set to position relative if it isn't already.
54914  * @param {Object} config A config object that sets properties on this grid.
54915  */
54916 Roo.grid.Grid = function(container, config){
54917         // initialize the container
54918         this.container = Roo.get(container);
54919         this.container.update("");
54920         this.container.setStyle("overflow", "hidden");
54921     this.container.addClass('x-grid-container');
54922
54923     this.id = this.container.id;
54924
54925     Roo.apply(this, config);
54926     // check and correct shorthanded configs
54927     if(this.ds){
54928         this.dataSource = this.ds;
54929         delete this.ds;
54930     }
54931     if(this.cm){
54932         this.colModel = this.cm;
54933         delete this.cm;
54934     }
54935     if(this.sm){
54936         this.selModel = this.sm;
54937         delete this.sm;
54938     }
54939
54940     if (this.selModel) {
54941         this.selModel = Roo.factory(this.selModel, Roo.grid);
54942         this.sm = this.selModel;
54943         this.sm.xmodule = this.xmodule || false;
54944     }
54945     if (typeof(this.colModel.config) == 'undefined') {
54946         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54947         this.cm = this.colModel;
54948         this.cm.xmodule = this.xmodule || false;
54949     }
54950     if (this.dataSource) {
54951         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54952         this.ds = this.dataSource;
54953         this.ds.xmodule = this.xmodule || false;
54954          
54955     }
54956     
54957     
54958     
54959     if(this.width){
54960         this.container.setWidth(this.width);
54961     }
54962
54963     if(this.height){
54964         this.container.setHeight(this.height);
54965     }
54966     /** @private */
54967         this.addEvents({
54968         // raw events
54969         /**
54970          * @event click
54971          * The raw click event for the entire grid.
54972          * @param {Roo.EventObject} e
54973          */
54974         "click" : true,
54975         /**
54976          * @event dblclick
54977          * The raw dblclick event for the entire grid.
54978          * @param {Roo.EventObject} e
54979          */
54980         "dblclick" : true,
54981         /**
54982          * @event contextmenu
54983          * The raw contextmenu event for the entire grid.
54984          * @param {Roo.EventObject} e
54985          */
54986         "contextmenu" : true,
54987         /**
54988          * @event mousedown
54989          * The raw mousedown event for the entire grid.
54990          * @param {Roo.EventObject} e
54991          */
54992         "mousedown" : true,
54993         /**
54994          * @event mouseup
54995          * The raw mouseup event for the entire grid.
54996          * @param {Roo.EventObject} e
54997          */
54998         "mouseup" : true,
54999         /**
55000          * @event mouseover
55001          * The raw mouseover event for the entire grid.
55002          * @param {Roo.EventObject} e
55003          */
55004         "mouseover" : true,
55005         /**
55006          * @event mouseout
55007          * The raw mouseout event for the entire grid.
55008          * @param {Roo.EventObject} e
55009          */
55010         "mouseout" : true,
55011         /**
55012          * @event keypress
55013          * The raw keypress event for the entire grid.
55014          * @param {Roo.EventObject} e
55015          */
55016         "keypress" : true,
55017         /**
55018          * @event keydown
55019          * The raw keydown event for the entire grid.
55020          * @param {Roo.EventObject} e
55021          */
55022         "keydown" : true,
55023
55024         // custom events
55025
55026         /**
55027          * @event cellclick
55028          * Fires when a cell is clicked
55029          * @param {Grid} this
55030          * @param {Number} rowIndex
55031          * @param {Number} columnIndex
55032          * @param {Roo.EventObject} e
55033          */
55034         "cellclick" : true,
55035         /**
55036          * @event celldblclick
55037          * Fires when a cell is double clicked
55038          * @param {Grid} this
55039          * @param {Number} rowIndex
55040          * @param {Number} columnIndex
55041          * @param {Roo.EventObject} e
55042          */
55043         "celldblclick" : true,
55044         /**
55045          * @event rowclick
55046          * Fires when a row is clicked
55047          * @param {Grid} this
55048          * @param {Number} rowIndex
55049          * @param {Roo.EventObject} e
55050          */
55051         "rowclick" : true,
55052         /**
55053          * @event rowdblclick
55054          * Fires when a row is double clicked
55055          * @param {Grid} this
55056          * @param {Number} rowIndex
55057          * @param {Roo.EventObject} e
55058          */
55059         "rowdblclick" : true,
55060         /**
55061          * @event headerclick
55062          * Fires when a header is clicked
55063          * @param {Grid} this
55064          * @param {Number} columnIndex
55065          * @param {Roo.EventObject} e
55066          */
55067         "headerclick" : true,
55068         /**
55069          * @event headerdblclick
55070          * Fires when a header cell is double clicked
55071          * @param {Grid} this
55072          * @param {Number} columnIndex
55073          * @param {Roo.EventObject} e
55074          */
55075         "headerdblclick" : true,
55076         /**
55077          * @event rowcontextmenu
55078          * Fires when a row is right clicked
55079          * @param {Grid} this
55080          * @param {Number} rowIndex
55081          * @param {Roo.EventObject} e
55082          */
55083         "rowcontextmenu" : true,
55084         /**
55085          * @event cellcontextmenu
55086          * Fires when a cell is right clicked
55087          * @param {Grid} this
55088          * @param {Number} rowIndex
55089          * @param {Number} cellIndex
55090          * @param {Roo.EventObject} e
55091          */
55092          "cellcontextmenu" : true,
55093         /**
55094          * @event headercontextmenu
55095          * Fires when a header is right clicked
55096          * @param {Grid} this
55097          * @param {Number} columnIndex
55098          * @param {Roo.EventObject} e
55099          */
55100         "headercontextmenu" : true,
55101         /**
55102          * @event bodyscroll
55103          * Fires when the body element is scrolled
55104          * @param {Number} scrollLeft
55105          * @param {Number} scrollTop
55106          */
55107         "bodyscroll" : true,
55108         /**
55109          * @event columnresize
55110          * Fires when the user resizes a column
55111          * @param {Number} columnIndex
55112          * @param {Number} newSize
55113          */
55114         "columnresize" : true,
55115         /**
55116          * @event columnmove
55117          * Fires when the user moves a column
55118          * @param {Number} oldIndex
55119          * @param {Number} newIndex
55120          */
55121         "columnmove" : true,
55122         /**
55123          * @event startdrag
55124          * Fires when row(s) start being dragged
55125          * @param {Grid} this
55126          * @param {Roo.GridDD} dd The drag drop object
55127          * @param {event} e The raw browser event
55128          */
55129         "startdrag" : true,
55130         /**
55131          * @event enddrag
55132          * Fires when a drag operation is complete
55133          * @param {Grid} this
55134          * @param {Roo.GridDD} dd The drag drop object
55135          * @param {event} e The raw browser event
55136          */
55137         "enddrag" : true,
55138         /**
55139          * @event dragdrop
55140          * Fires when dragged row(s) are dropped on a valid DD target
55141          * @param {Grid} this
55142          * @param {Roo.GridDD} dd The drag drop object
55143          * @param {String} targetId The target drag drop object
55144          * @param {event} e The raw browser event
55145          */
55146         "dragdrop" : true,
55147         /**
55148          * @event dragover
55149          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55150          * @param {Grid} this
55151          * @param {Roo.GridDD} dd The drag drop object
55152          * @param {String} targetId The target drag drop object
55153          * @param {event} e The raw browser event
55154          */
55155         "dragover" : true,
55156         /**
55157          * @event dragenter
55158          *  Fires when the dragged row(s) first cross another DD target while being dragged
55159          * @param {Grid} this
55160          * @param {Roo.GridDD} dd The drag drop object
55161          * @param {String} targetId The target drag drop object
55162          * @param {event} e The raw browser event
55163          */
55164         "dragenter" : true,
55165         /**
55166          * @event dragout
55167          * Fires when the dragged row(s) leave another DD target while being dragged
55168          * @param {Grid} this
55169          * @param {Roo.GridDD} dd The drag drop object
55170          * @param {String} targetId The target drag drop object
55171          * @param {event} e The raw browser event
55172          */
55173         "dragout" : true,
55174         /**
55175          * @event rowclass
55176          * Fires when a row is rendered, so you can change add a style to it.
55177          * @param {GridView} gridview   The grid view
55178          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55179          */
55180         'rowclass' : true,
55181
55182         /**
55183          * @event render
55184          * Fires when the grid is rendered
55185          * @param {Grid} grid
55186          */
55187         'render' : true
55188     });
55189
55190     Roo.grid.Grid.superclass.constructor.call(this);
55191 };
55192 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55193     
55194     /**
55195      * @cfg {String} ddGroup - drag drop group.
55196      */
55197
55198     /**
55199      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55200      */
55201     minColumnWidth : 25,
55202
55203     /**
55204      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55205      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55206      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55207      */
55208     autoSizeColumns : false,
55209
55210     /**
55211      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55212      */
55213     autoSizeHeaders : true,
55214
55215     /**
55216      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55217      */
55218     monitorWindowResize : true,
55219
55220     /**
55221      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55222      * rows measured to get a columns size. Default is 0 (all rows).
55223      */
55224     maxRowsToMeasure : 0,
55225
55226     /**
55227      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55228      */
55229     trackMouseOver : true,
55230
55231     /**
55232     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55233     */
55234     
55235     /**
55236     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55237     */
55238     enableDragDrop : false,
55239     
55240     /**
55241     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55242     */
55243     enableColumnMove : true,
55244     
55245     /**
55246     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55247     */
55248     enableColumnHide : true,
55249     
55250     /**
55251     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55252     */
55253     enableRowHeightSync : false,
55254     
55255     /**
55256     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55257     */
55258     stripeRows : true,
55259     
55260     /**
55261     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55262     */
55263     autoHeight : false,
55264
55265     /**
55266      * @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.
55267      */
55268     autoExpandColumn : false,
55269
55270     /**
55271     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55272     * Default is 50.
55273     */
55274     autoExpandMin : 50,
55275
55276     /**
55277     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55278     */
55279     autoExpandMax : 1000,
55280
55281     /**
55282     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55283     */
55284     view : null,
55285
55286     /**
55287     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55288     */
55289     loadMask : false,
55290     /**
55291     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55292     */
55293     dropTarget: false,
55294     
55295    
55296     
55297     // private
55298     rendered : false,
55299
55300     /**
55301     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55302     * of a fixed width. Default is false.
55303     */
55304     /**
55305     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55306     */
55307     /**
55308      * Called once after all setup has been completed and the grid is ready to be rendered.
55309      * @return {Roo.grid.Grid} this
55310      */
55311     render : function()
55312     {
55313         var c = this.container;
55314         // try to detect autoHeight/width mode
55315         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55316             this.autoHeight = true;
55317         }
55318         var view = this.getView();
55319         view.init(this);
55320
55321         c.on("click", this.onClick, this);
55322         c.on("dblclick", this.onDblClick, this);
55323         c.on("contextmenu", this.onContextMenu, this);
55324         c.on("keydown", this.onKeyDown, this);
55325         if (Roo.isTouch) {
55326             c.on("touchstart", this.onTouchStart, this);
55327         }
55328
55329         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55330
55331         this.getSelectionModel().init(this);
55332
55333         view.render();
55334
55335         if(this.loadMask){
55336             this.loadMask = new Roo.LoadMask(this.container,
55337                     Roo.apply({store:this.dataSource}, this.loadMask));
55338         }
55339         
55340         
55341         if (this.toolbar && this.toolbar.xtype) {
55342             this.toolbar.container = this.getView().getHeaderPanel(true);
55343             this.toolbar = new Roo.Toolbar(this.toolbar);
55344         }
55345         if (this.footer && this.footer.xtype) {
55346             this.footer.dataSource = this.getDataSource();
55347             this.footer.container = this.getView().getFooterPanel(true);
55348             this.footer = Roo.factory(this.footer, Roo);
55349         }
55350         if (this.dropTarget && this.dropTarget.xtype) {
55351             delete this.dropTarget.xtype;
55352             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55353         }
55354         
55355         
55356         this.rendered = true;
55357         this.fireEvent('render', this);
55358         return this;
55359     },
55360
55361         /**
55362          * Reconfigures the grid to use a different Store and Column Model.
55363          * The View will be bound to the new objects and refreshed.
55364          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55365          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55366          */
55367     reconfigure : function(dataSource, colModel){
55368         if(this.loadMask){
55369             this.loadMask.destroy();
55370             this.loadMask = new Roo.LoadMask(this.container,
55371                     Roo.apply({store:dataSource}, this.loadMask));
55372         }
55373         this.view.bind(dataSource, colModel);
55374         this.dataSource = dataSource;
55375         this.colModel = colModel;
55376         this.view.refresh(true);
55377     },
55378
55379     // private
55380     onKeyDown : function(e){
55381         this.fireEvent("keydown", e);
55382     },
55383
55384     /**
55385      * Destroy this grid.
55386      * @param {Boolean} removeEl True to remove the element
55387      */
55388     destroy : function(removeEl, keepListeners){
55389         if(this.loadMask){
55390             this.loadMask.destroy();
55391         }
55392         var c = this.container;
55393         c.removeAllListeners();
55394         this.view.destroy();
55395         this.colModel.purgeListeners();
55396         if(!keepListeners){
55397             this.purgeListeners();
55398         }
55399         c.update("");
55400         if(removeEl === true){
55401             c.remove();
55402         }
55403     },
55404
55405     // private
55406     processEvent : function(name, e){
55407         // does this fire select???
55408         //Roo.log('grid:processEvent '  + name);
55409         
55410         if (name != 'touchstart' ) {
55411             this.fireEvent(name, e);    
55412         }
55413         
55414         var t = e.getTarget();
55415         var v = this.view;
55416         var header = v.findHeaderIndex(t);
55417         if(header !== false){
55418             var ename = name == 'touchstart' ? 'click' : name;
55419              
55420             this.fireEvent("header" + ename, this, header, e);
55421         }else{
55422             var row = v.findRowIndex(t);
55423             var cell = v.findCellIndex(t);
55424             if (name == 'touchstart') {
55425                 // first touch is always a click.
55426                 // hopefull this happens after selection is updated.?
55427                 name = false;
55428                 
55429                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55430                     var cs = this.selModel.getSelectedCell();
55431                     if (row == cs[0] && cell == cs[1]){
55432                         name = 'dblclick';
55433                     }
55434                 }
55435                 if (typeof(this.selModel.getSelections) != 'undefined') {
55436                     var cs = this.selModel.getSelections();
55437                     var ds = this.dataSource;
55438                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55439                         name = 'dblclick';
55440                     }
55441                 }
55442                 if (!name) {
55443                     return;
55444                 }
55445             }
55446             
55447             
55448             if(row !== false){
55449                 this.fireEvent("row" + name, this, row, e);
55450                 if(cell !== false){
55451                     this.fireEvent("cell" + name, this, row, cell, e);
55452                 }
55453             }
55454         }
55455     },
55456
55457     // private
55458     onClick : function(e){
55459         this.processEvent("click", e);
55460     },
55461    // private
55462     onTouchStart : function(e){
55463         this.processEvent("touchstart", e);
55464     },
55465
55466     // private
55467     onContextMenu : function(e, t){
55468         this.processEvent("contextmenu", e);
55469     },
55470
55471     // private
55472     onDblClick : function(e){
55473         this.processEvent("dblclick", e);
55474     },
55475
55476     // private
55477     walkCells : function(row, col, step, fn, scope){
55478         var cm = this.colModel, clen = cm.getColumnCount();
55479         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55480         if(step < 0){
55481             if(col < 0){
55482                 row--;
55483                 first = false;
55484             }
55485             while(row >= 0){
55486                 if(!first){
55487                     col = clen-1;
55488                 }
55489                 first = false;
55490                 while(col >= 0){
55491                     if(fn.call(scope || this, row, col, cm) === true){
55492                         return [row, col];
55493                     }
55494                     col--;
55495                 }
55496                 row--;
55497             }
55498         } else {
55499             if(col >= clen){
55500                 row++;
55501                 first = false;
55502             }
55503             while(row < rlen){
55504                 if(!first){
55505                     col = 0;
55506                 }
55507                 first = false;
55508                 while(col < clen){
55509                     if(fn.call(scope || this, row, col, cm) === true){
55510                         return [row, col];
55511                     }
55512                     col++;
55513                 }
55514                 row++;
55515             }
55516         }
55517         return null;
55518     },
55519
55520     // private
55521     getSelections : function(){
55522         return this.selModel.getSelections();
55523     },
55524
55525     /**
55526      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55527      * but if manual update is required this method will initiate it.
55528      */
55529     autoSize : function(){
55530         if(this.rendered){
55531             this.view.layout();
55532             if(this.view.adjustForScroll){
55533                 this.view.adjustForScroll();
55534             }
55535         }
55536     },
55537
55538     /**
55539      * Returns the grid's underlying element.
55540      * @return {Element} The element
55541      */
55542     getGridEl : function(){
55543         return this.container;
55544     },
55545
55546     // private for compatibility, overridden by editor grid
55547     stopEditing : function(){},
55548
55549     /**
55550      * Returns the grid's SelectionModel.
55551      * @return {SelectionModel}
55552      */
55553     getSelectionModel : function(){
55554         if(!this.selModel){
55555             this.selModel = new Roo.grid.RowSelectionModel();
55556         }
55557         return this.selModel;
55558     },
55559
55560     /**
55561      * Returns the grid's DataSource.
55562      * @return {DataSource}
55563      */
55564     getDataSource : function(){
55565         return this.dataSource;
55566     },
55567
55568     /**
55569      * Returns the grid's ColumnModel.
55570      * @return {ColumnModel}
55571      */
55572     getColumnModel : function(){
55573         return this.colModel;
55574     },
55575
55576     /**
55577      * Returns the grid's GridView object.
55578      * @return {GridView}
55579      */
55580     getView : function(){
55581         if(!this.view){
55582             this.view = new Roo.grid.GridView(this.viewConfig);
55583         }
55584         return this.view;
55585     },
55586     /**
55587      * Called to get grid's drag proxy text, by default returns this.ddText.
55588      * @return {String}
55589      */
55590     getDragDropText : function(){
55591         var count = this.selModel.getCount();
55592         return String.format(this.ddText, count, count == 1 ? '' : 's');
55593     }
55594 });
55595 /**
55596  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55597  * %0 is replaced with the number of selected rows.
55598  * @type String
55599  */
55600 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55601  * Based on:
55602  * Ext JS Library 1.1.1
55603  * Copyright(c) 2006-2007, Ext JS, LLC.
55604  *
55605  * Originally Released Under LGPL - original licence link has changed is not relivant.
55606  *
55607  * Fork - LGPL
55608  * <script type="text/javascript">
55609  */
55610  
55611 Roo.grid.AbstractGridView = function(){
55612         this.grid = null;
55613         
55614         this.events = {
55615             "beforerowremoved" : true,
55616             "beforerowsinserted" : true,
55617             "beforerefresh" : true,
55618             "rowremoved" : true,
55619             "rowsinserted" : true,
55620             "rowupdated" : true,
55621             "refresh" : true
55622         };
55623     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55624 };
55625
55626 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55627     rowClass : "x-grid-row",
55628     cellClass : "x-grid-cell",
55629     tdClass : "x-grid-td",
55630     hdClass : "x-grid-hd",
55631     splitClass : "x-grid-hd-split",
55632     
55633     init: function(grid){
55634         this.grid = grid;
55635                 var cid = this.grid.getGridEl().id;
55636         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55637         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55638         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55639         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55640         },
55641         
55642     getColumnRenderers : function(){
55643         var renderers = [];
55644         var cm = this.grid.colModel;
55645         var colCount = cm.getColumnCount();
55646         for(var i = 0; i < colCount; i++){
55647             renderers[i] = cm.getRenderer(i);
55648         }
55649         return renderers;
55650     },
55651     
55652     getColumnIds : function(){
55653         var ids = [];
55654         var cm = this.grid.colModel;
55655         var colCount = cm.getColumnCount();
55656         for(var i = 0; i < colCount; i++){
55657             ids[i] = cm.getColumnId(i);
55658         }
55659         return ids;
55660     },
55661     
55662     getDataIndexes : function(){
55663         if(!this.indexMap){
55664             this.indexMap = this.buildIndexMap();
55665         }
55666         return this.indexMap.colToData;
55667     },
55668     
55669     getColumnIndexByDataIndex : function(dataIndex){
55670         if(!this.indexMap){
55671             this.indexMap = this.buildIndexMap();
55672         }
55673         return this.indexMap.dataToCol[dataIndex];
55674     },
55675     
55676     /**
55677      * Set a css style for a column dynamically. 
55678      * @param {Number} colIndex The index of the column
55679      * @param {String} name The css property name
55680      * @param {String} value The css value
55681      */
55682     setCSSStyle : function(colIndex, name, value){
55683         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55684         Roo.util.CSS.updateRule(selector, name, value);
55685     },
55686     
55687     generateRules : function(cm){
55688         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55689         Roo.util.CSS.removeStyleSheet(rulesId);
55690         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55691             var cid = cm.getColumnId(i);
55692             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55693                          this.tdSelector, cid, " {\n}\n",
55694                          this.hdSelector, cid, " {\n}\n",
55695                          this.splitSelector, cid, " {\n}\n");
55696         }
55697         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55698     }
55699 });/*
55700  * Based on:
55701  * Ext JS Library 1.1.1
55702  * Copyright(c) 2006-2007, Ext JS, LLC.
55703  *
55704  * Originally Released Under LGPL - original licence link has changed is not relivant.
55705  *
55706  * Fork - LGPL
55707  * <script type="text/javascript">
55708  */
55709
55710 // private
55711 // This is a support class used internally by the Grid components
55712 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55713     this.grid = grid;
55714     this.view = grid.getView();
55715     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55716     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55717     if(hd2){
55718         this.setHandleElId(Roo.id(hd));
55719         this.setOuterHandleElId(Roo.id(hd2));
55720     }
55721     this.scroll = false;
55722 };
55723 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55724     maxDragWidth: 120,
55725     getDragData : function(e){
55726         var t = Roo.lib.Event.getTarget(e);
55727         var h = this.view.findHeaderCell(t);
55728         if(h){
55729             return {ddel: h.firstChild, header:h};
55730         }
55731         return false;
55732     },
55733
55734     onInitDrag : function(e){
55735         this.view.headersDisabled = true;
55736         var clone = this.dragData.ddel.cloneNode(true);
55737         clone.id = Roo.id();
55738         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55739         this.proxy.update(clone);
55740         return true;
55741     },
55742
55743     afterValidDrop : function(){
55744         var v = this.view;
55745         setTimeout(function(){
55746             v.headersDisabled = false;
55747         }, 50);
55748     },
55749
55750     afterInvalidDrop : function(){
55751         var v = this.view;
55752         setTimeout(function(){
55753             v.headersDisabled = false;
55754         }, 50);
55755     }
55756 });
55757 /*
55758  * Based on:
55759  * Ext JS Library 1.1.1
55760  * Copyright(c) 2006-2007, Ext JS, LLC.
55761  *
55762  * Originally Released Under LGPL - original licence link has changed is not relivant.
55763  *
55764  * Fork - LGPL
55765  * <script type="text/javascript">
55766  */
55767 // private
55768 // This is a support class used internally by the Grid components
55769 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55770     this.grid = grid;
55771     this.view = grid.getView();
55772     // split the proxies so they don't interfere with mouse events
55773     this.proxyTop = Roo.DomHelper.append(document.body, {
55774         cls:"col-move-top", html:"&#160;"
55775     }, true);
55776     this.proxyBottom = Roo.DomHelper.append(document.body, {
55777         cls:"col-move-bottom", html:"&#160;"
55778     }, true);
55779     this.proxyTop.hide = this.proxyBottom.hide = function(){
55780         this.setLeftTop(-100,-100);
55781         this.setStyle("visibility", "hidden");
55782     };
55783     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55784     // temporarily disabled
55785     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55786     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55787 };
55788 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55789     proxyOffsets : [-4, -9],
55790     fly: Roo.Element.fly,
55791
55792     getTargetFromEvent : function(e){
55793         var t = Roo.lib.Event.getTarget(e);
55794         var cindex = this.view.findCellIndex(t);
55795         if(cindex !== false){
55796             return this.view.getHeaderCell(cindex);
55797         }
55798         return null;
55799     },
55800
55801     nextVisible : function(h){
55802         var v = this.view, cm = this.grid.colModel;
55803         h = h.nextSibling;
55804         while(h){
55805             if(!cm.isHidden(v.getCellIndex(h))){
55806                 return h;
55807             }
55808             h = h.nextSibling;
55809         }
55810         return null;
55811     },
55812
55813     prevVisible : function(h){
55814         var v = this.view, cm = this.grid.colModel;
55815         h = h.prevSibling;
55816         while(h){
55817             if(!cm.isHidden(v.getCellIndex(h))){
55818                 return h;
55819             }
55820             h = h.prevSibling;
55821         }
55822         return null;
55823     },
55824
55825     positionIndicator : function(h, n, e){
55826         var x = Roo.lib.Event.getPageX(e);
55827         var r = Roo.lib.Dom.getRegion(n.firstChild);
55828         var px, pt, py = r.top + this.proxyOffsets[1];
55829         if((r.right - x) <= (r.right-r.left)/2){
55830             px = r.right+this.view.borderWidth;
55831             pt = "after";
55832         }else{
55833             px = r.left;
55834             pt = "before";
55835         }
55836         var oldIndex = this.view.getCellIndex(h);
55837         var newIndex = this.view.getCellIndex(n);
55838
55839         if(this.grid.colModel.isFixed(newIndex)){
55840             return false;
55841         }
55842
55843         var locked = this.grid.colModel.isLocked(newIndex);
55844
55845         if(pt == "after"){
55846             newIndex++;
55847         }
55848         if(oldIndex < newIndex){
55849             newIndex--;
55850         }
55851         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55852             return false;
55853         }
55854         px +=  this.proxyOffsets[0];
55855         this.proxyTop.setLeftTop(px, py);
55856         this.proxyTop.show();
55857         if(!this.bottomOffset){
55858             this.bottomOffset = this.view.mainHd.getHeight();
55859         }
55860         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55861         this.proxyBottom.show();
55862         return pt;
55863     },
55864
55865     onNodeEnter : function(n, dd, e, data){
55866         if(data.header != n){
55867             this.positionIndicator(data.header, n, e);
55868         }
55869     },
55870
55871     onNodeOver : function(n, dd, e, data){
55872         var result = false;
55873         if(data.header != n){
55874             result = this.positionIndicator(data.header, n, e);
55875         }
55876         if(!result){
55877             this.proxyTop.hide();
55878             this.proxyBottom.hide();
55879         }
55880         return result ? this.dropAllowed : this.dropNotAllowed;
55881     },
55882
55883     onNodeOut : function(n, dd, e, data){
55884         this.proxyTop.hide();
55885         this.proxyBottom.hide();
55886     },
55887
55888     onNodeDrop : function(n, dd, e, data){
55889         var h = data.header;
55890         if(h != n){
55891             var cm = this.grid.colModel;
55892             var x = Roo.lib.Event.getPageX(e);
55893             var r = Roo.lib.Dom.getRegion(n.firstChild);
55894             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55895             var oldIndex = this.view.getCellIndex(h);
55896             var newIndex = this.view.getCellIndex(n);
55897             var locked = cm.isLocked(newIndex);
55898             if(pt == "after"){
55899                 newIndex++;
55900             }
55901             if(oldIndex < newIndex){
55902                 newIndex--;
55903             }
55904             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55905                 return false;
55906             }
55907             cm.setLocked(oldIndex, locked, true);
55908             cm.moveColumn(oldIndex, newIndex);
55909             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55910             return true;
55911         }
55912         return false;
55913     }
55914 });
55915 /*
55916  * Based on:
55917  * Ext JS Library 1.1.1
55918  * Copyright(c) 2006-2007, Ext JS, LLC.
55919  *
55920  * Originally Released Under LGPL - original licence link has changed is not relivant.
55921  *
55922  * Fork - LGPL
55923  * <script type="text/javascript">
55924  */
55925   
55926 /**
55927  * @class Roo.grid.GridView
55928  * @extends Roo.util.Observable
55929  *
55930  * @constructor
55931  * @param {Object} config
55932  */
55933 Roo.grid.GridView = function(config){
55934     Roo.grid.GridView.superclass.constructor.call(this);
55935     this.el = null;
55936
55937     Roo.apply(this, config);
55938 };
55939
55940 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55941
55942     unselectable :  'unselectable="on"',
55943     unselectableCls :  'x-unselectable',
55944     
55945     
55946     rowClass : "x-grid-row",
55947
55948     cellClass : "x-grid-col",
55949
55950     tdClass : "x-grid-td",
55951
55952     hdClass : "x-grid-hd",
55953
55954     splitClass : "x-grid-split",
55955
55956     sortClasses : ["sort-asc", "sort-desc"],
55957
55958     enableMoveAnim : false,
55959
55960     hlColor: "C3DAF9",
55961
55962     dh : Roo.DomHelper,
55963
55964     fly : Roo.Element.fly,
55965
55966     css : Roo.util.CSS,
55967
55968     borderWidth: 1,
55969
55970     splitOffset: 3,
55971
55972     scrollIncrement : 22,
55973
55974     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55975
55976     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55977
55978     bind : function(ds, cm){
55979         if(this.ds){
55980             this.ds.un("load", this.onLoad, this);
55981             this.ds.un("datachanged", this.onDataChange, this);
55982             this.ds.un("add", this.onAdd, this);
55983             this.ds.un("remove", this.onRemove, this);
55984             this.ds.un("update", this.onUpdate, this);
55985             this.ds.un("clear", this.onClear, this);
55986         }
55987         if(ds){
55988             ds.on("load", this.onLoad, this);
55989             ds.on("datachanged", this.onDataChange, this);
55990             ds.on("add", this.onAdd, this);
55991             ds.on("remove", this.onRemove, this);
55992             ds.on("update", this.onUpdate, this);
55993             ds.on("clear", this.onClear, this);
55994         }
55995         this.ds = ds;
55996
55997         if(this.cm){
55998             this.cm.un("widthchange", this.onColWidthChange, this);
55999             this.cm.un("headerchange", this.onHeaderChange, this);
56000             this.cm.un("hiddenchange", this.onHiddenChange, this);
56001             this.cm.un("columnmoved", this.onColumnMove, this);
56002             this.cm.un("columnlockchange", this.onColumnLock, this);
56003         }
56004         if(cm){
56005             this.generateRules(cm);
56006             cm.on("widthchange", this.onColWidthChange, this);
56007             cm.on("headerchange", this.onHeaderChange, this);
56008             cm.on("hiddenchange", this.onHiddenChange, this);
56009             cm.on("columnmoved", this.onColumnMove, this);
56010             cm.on("columnlockchange", this.onColumnLock, this);
56011         }
56012         this.cm = cm;
56013     },
56014
56015     init: function(grid){
56016         Roo.grid.GridView.superclass.init.call(this, grid);
56017
56018         this.bind(grid.dataSource, grid.colModel);
56019
56020         grid.on("headerclick", this.handleHeaderClick, this);
56021
56022         if(grid.trackMouseOver){
56023             grid.on("mouseover", this.onRowOver, this);
56024             grid.on("mouseout", this.onRowOut, this);
56025         }
56026         grid.cancelTextSelection = function(){};
56027         this.gridId = grid.id;
56028
56029         var tpls = this.templates || {};
56030
56031         if(!tpls.master){
56032             tpls.master = new Roo.Template(
56033                '<div class="x-grid" hidefocus="true">',
56034                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56035                   '<div class="x-grid-topbar"></div>',
56036                   '<div class="x-grid-scroller"><div></div></div>',
56037                   '<div class="x-grid-locked">',
56038                       '<div class="x-grid-header">{lockedHeader}</div>',
56039                       '<div class="x-grid-body">{lockedBody}</div>',
56040                   "</div>",
56041                   '<div class="x-grid-viewport">',
56042                       '<div class="x-grid-header">{header}</div>',
56043                       '<div class="x-grid-body">{body}</div>',
56044                   "</div>",
56045                   '<div class="x-grid-bottombar"></div>',
56046                  
56047                   '<div class="x-grid-resize-proxy">&#160;</div>',
56048                "</div>"
56049             );
56050             tpls.master.disableformats = true;
56051         }
56052
56053         if(!tpls.header){
56054             tpls.header = new Roo.Template(
56055                '<table border="0" cellspacing="0" cellpadding="0">',
56056                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56057                "</table>{splits}"
56058             );
56059             tpls.header.disableformats = true;
56060         }
56061         tpls.header.compile();
56062
56063         if(!tpls.hcell){
56064             tpls.hcell = new Roo.Template(
56065                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56066                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56067                 "</div></td>"
56068              );
56069              tpls.hcell.disableFormats = true;
56070         }
56071         tpls.hcell.compile();
56072
56073         if(!tpls.hsplit){
56074             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56075                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56076             tpls.hsplit.disableFormats = true;
56077         }
56078         tpls.hsplit.compile();
56079
56080         if(!tpls.body){
56081             tpls.body = new Roo.Template(
56082                '<table border="0" cellspacing="0" cellpadding="0">',
56083                "<tbody>{rows}</tbody>",
56084                "</table>"
56085             );
56086             tpls.body.disableFormats = true;
56087         }
56088         tpls.body.compile();
56089
56090         if(!tpls.row){
56091             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56092             tpls.row.disableFormats = true;
56093         }
56094         tpls.row.compile();
56095
56096         if(!tpls.cell){
56097             tpls.cell = new Roo.Template(
56098                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56099                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56100                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56101                 "</td>"
56102             );
56103             tpls.cell.disableFormats = true;
56104         }
56105         tpls.cell.compile();
56106
56107         this.templates = tpls;
56108     },
56109
56110     // remap these for backwards compat
56111     onColWidthChange : function(){
56112         this.updateColumns.apply(this, arguments);
56113     },
56114     onHeaderChange : function(){
56115         this.updateHeaders.apply(this, arguments);
56116     }, 
56117     onHiddenChange : function(){
56118         this.handleHiddenChange.apply(this, arguments);
56119     },
56120     onColumnMove : function(){
56121         this.handleColumnMove.apply(this, arguments);
56122     },
56123     onColumnLock : function(){
56124         this.handleLockChange.apply(this, arguments);
56125     },
56126
56127     onDataChange : function(){
56128         this.refresh();
56129         this.updateHeaderSortState();
56130     },
56131
56132     onClear : function(){
56133         this.refresh();
56134     },
56135
56136     onUpdate : function(ds, record){
56137         this.refreshRow(record);
56138     },
56139
56140     refreshRow : function(record){
56141         var ds = this.ds, index;
56142         if(typeof record == 'number'){
56143             index = record;
56144             record = ds.getAt(index);
56145         }else{
56146             index = ds.indexOf(record);
56147         }
56148         this.insertRows(ds, index, index, true);
56149         this.onRemove(ds, record, index+1, true);
56150         this.syncRowHeights(index, index);
56151         this.layout();
56152         this.fireEvent("rowupdated", this, index, record);
56153     },
56154
56155     onAdd : function(ds, records, index){
56156         this.insertRows(ds, index, index + (records.length-1));
56157     },
56158
56159     onRemove : function(ds, record, index, isUpdate){
56160         if(isUpdate !== true){
56161             this.fireEvent("beforerowremoved", this, index, record);
56162         }
56163         var bt = this.getBodyTable(), lt = this.getLockedTable();
56164         if(bt.rows[index]){
56165             bt.firstChild.removeChild(bt.rows[index]);
56166         }
56167         if(lt.rows[index]){
56168             lt.firstChild.removeChild(lt.rows[index]);
56169         }
56170         if(isUpdate !== true){
56171             this.stripeRows(index);
56172             this.syncRowHeights(index, index);
56173             this.layout();
56174             this.fireEvent("rowremoved", this, index, record);
56175         }
56176     },
56177
56178     onLoad : function(){
56179         this.scrollToTop();
56180     },
56181
56182     /**
56183      * Scrolls the grid to the top
56184      */
56185     scrollToTop : function(){
56186         if(this.scroller){
56187             this.scroller.dom.scrollTop = 0;
56188             this.syncScroll();
56189         }
56190     },
56191
56192     /**
56193      * Gets a panel in the header of the grid that can be used for toolbars etc.
56194      * After modifying the contents of this panel a call to grid.autoSize() may be
56195      * required to register any changes in size.
56196      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56197      * @return Roo.Element
56198      */
56199     getHeaderPanel : function(doShow){
56200         if(doShow){
56201             this.headerPanel.show();
56202         }
56203         return this.headerPanel;
56204     },
56205
56206     /**
56207      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56208      * After modifying the contents of this panel a call to grid.autoSize() may be
56209      * required to register any changes in size.
56210      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56211      * @return Roo.Element
56212      */
56213     getFooterPanel : function(doShow){
56214         if(doShow){
56215             this.footerPanel.show();
56216         }
56217         return this.footerPanel;
56218     },
56219
56220     initElements : function(){
56221         var E = Roo.Element;
56222         var el = this.grid.getGridEl().dom.firstChild;
56223         var cs = el.childNodes;
56224
56225         this.el = new E(el);
56226         
56227          this.focusEl = new E(el.firstChild);
56228         this.focusEl.swallowEvent("click", true);
56229         
56230         this.headerPanel = new E(cs[1]);
56231         this.headerPanel.enableDisplayMode("block");
56232
56233         this.scroller = new E(cs[2]);
56234         this.scrollSizer = new E(this.scroller.dom.firstChild);
56235
56236         this.lockedWrap = new E(cs[3]);
56237         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56238         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56239
56240         this.mainWrap = new E(cs[4]);
56241         this.mainHd = new E(this.mainWrap.dom.firstChild);
56242         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56243
56244         this.footerPanel = new E(cs[5]);
56245         this.footerPanel.enableDisplayMode("block");
56246
56247         this.resizeProxy = new E(cs[6]);
56248
56249         this.headerSelector = String.format(
56250            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56251            this.lockedHd.id, this.mainHd.id
56252         );
56253
56254         this.splitterSelector = String.format(
56255            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56256            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56257         );
56258     },
56259     idToCssName : function(s)
56260     {
56261         return s.replace(/[^a-z0-9]+/ig, '-');
56262     },
56263
56264     getHeaderCell : function(index){
56265         return Roo.DomQuery.select(this.headerSelector)[index];
56266     },
56267
56268     getHeaderCellMeasure : function(index){
56269         return this.getHeaderCell(index).firstChild;
56270     },
56271
56272     getHeaderCellText : function(index){
56273         return this.getHeaderCell(index).firstChild.firstChild;
56274     },
56275
56276     getLockedTable : function(){
56277         return this.lockedBody.dom.firstChild;
56278     },
56279
56280     getBodyTable : function(){
56281         return this.mainBody.dom.firstChild;
56282     },
56283
56284     getLockedRow : function(index){
56285         return this.getLockedTable().rows[index];
56286     },
56287
56288     getRow : function(index){
56289         return this.getBodyTable().rows[index];
56290     },
56291
56292     getRowComposite : function(index){
56293         if(!this.rowEl){
56294             this.rowEl = new Roo.CompositeElementLite();
56295         }
56296         var els = [], lrow, mrow;
56297         if(lrow = this.getLockedRow(index)){
56298             els.push(lrow);
56299         }
56300         if(mrow = this.getRow(index)){
56301             els.push(mrow);
56302         }
56303         this.rowEl.elements = els;
56304         return this.rowEl;
56305     },
56306     /**
56307      * Gets the 'td' of the cell
56308      * 
56309      * @param {Integer} rowIndex row to select
56310      * @param {Integer} colIndex column to select
56311      * 
56312      * @return {Object} 
56313      */
56314     getCell : function(rowIndex, colIndex){
56315         var locked = this.cm.getLockedCount();
56316         var source;
56317         if(colIndex < locked){
56318             source = this.lockedBody.dom.firstChild;
56319         }else{
56320             source = this.mainBody.dom.firstChild;
56321             colIndex -= locked;
56322         }
56323         return source.rows[rowIndex].childNodes[colIndex];
56324     },
56325
56326     getCellText : function(rowIndex, colIndex){
56327         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56328     },
56329
56330     getCellBox : function(cell){
56331         var b = this.fly(cell).getBox();
56332         if(Roo.isOpera){ // opera fails to report the Y
56333             b.y = cell.offsetTop + this.mainBody.getY();
56334         }
56335         return b;
56336     },
56337
56338     getCellIndex : function(cell){
56339         var id = String(cell.className).match(this.cellRE);
56340         if(id){
56341             return parseInt(id[1], 10);
56342         }
56343         return 0;
56344     },
56345
56346     findHeaderIndex : function(n){
56347         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56348         return r ? this.getCellIndex(r) : false;
56349     },
56350
56351     findHeaderCell : function(n){
56352         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56353         return r ? r : false;
56354     },
56355
56356     findRowIndex : function(n){
56357         if(!n){
56358             return false;
56359         }
56360         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56361         return r ? r.rowIndex : false;
56362     },
56363
56364     findCellIndex : function(node){
56365         var stop = this.el.dom;
56366         while(node && node != stop){
56367             if(this.findRE.test(node.className)){
56368                 return this.getCellIndex(node);
56369             }
56370             node = node.parentNode;
56371         }
56372         return false;
56373     },
56374
56375     getColumnId : function(index){
56376         return this.cm.getColumnId(index);
56377     },
56378
56379     getSplitters : function()
56380     {
56381         if(this.splitterSelector){
56382            return Roo.DomQuery.select(this.splitterSelector);
56383         }else{
56384             return null;
56385       }
56386     },
56387
56388     getSplitter : function(index){
56389         return this.getSplitters()[index];
56390     },
56391
56392     onRowOver : function(e, t){
56393         var row;
56394         if((row = this.findRowIndex(t)) !== false){
56395             this.getRowComposite(row).addClass("x-grid-row-over");
56396         }
56397     },
56398
56399     onRowOut : function(e, t){
56400         var row;
56401         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56402             this.getRowComposite(row).removeClass("x-grid-row-over");
56403         }
56404     },
56405
56406     renderHeaders : function(){
56407         var cm = this.cm;
56408         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56409         var cb = [], lb = [], sb = [], lsb = [], p = {};
56410         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56411             p.cellId = "x-grid-hd-0-" + i;
56412             p.splitId = "x-grid-csplit-0-" + i;
56413             p.id = cm.getColumnId(i);
56414             p.value = cm.getColumnHeader(i) || "";
56415             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56416             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56417             if(!cm.isLocked(i)){
56418                 cb[cb.length] = ct.apply(p);
56419                 sb[sb.length] = st.apply(p);
56420             }else{
56421                 lb[lb.length] = ct.apply(p);
56422                 lsb[lsb.length] = st.apply(p);
56423             }
56424         }
56425         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56426                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56427     },
56428
56429     updateHeaders : function(){
56430         var html = this.renderHeaders();
56431         this.lockedHd.update(html[0]);
56432         this.mainHd.update(html[1]);
56433     },
56434
56435     /**
56436      * Focuses the specified row.
56437      * @param {Number} row The row index
56438      */
56439     focusRow : function(row)
56440     {
56441         //Roo.log('GridView.focusRow');
56442         var x = this.scroller.dom.scrollLeft;
56443         this.focusCell(row, 0, false);
56444         this.scroller.dom.scrollLeft = x;
56445     },
56446
56447     /**
56448      * Focuses the specified cell.
56449      * @param {Number} row The row index
56450      * @param {Number} col The column index
56451      * @param {Boolean} hscroll false to disable horizontal scrolling
56452      */
56453     focusCell : function(row, col, hscroll)
56454     {
56455         //Roo.log('GridView.focusCell');
56456         var el = this.ensureVisible(row, col, hscroll);
56457         this.focusEl.alignTo(el, "tl-tl");
56458         if(Roo.isGecko){
56459             this.focusEl.focus();
56460         }else{
56461             this.focusEl.focus.defer(1, this.focusEl);
56462         }
56463     },
56464
56465     /**
56466      * Scrolls the specified cell into view
56467      * @param {Number} row The row index
56468      * @param {Number} col The column index
56469      * @param {Boolean} hscroll false to disable horizontal scrolling
56470      */
56471     ensureVisible : function(row, col, hscroll)
56472     {
56473         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56474         //return null; //disable for testing.
56475         if(typeof row != "number"){
56476             row = row.rowIndex;
56477         }
56478         if(row < 0 && row >= this.ds.getCount()){
56479             return  null;
56480         }
56481         col = (col !== undefined ? col : 0);
56482         var cm = this.grid.colModel;
56483         while(cm.isHidden(col)){
56484             col++;
56485         }
56486
56487         var el = this.getCell(row, col);
56488         if(!el){
56489             return null;
56490         }
56491         var c = this.scroller.dom;
56492
56493         var ctop = parseInt(el.offsetTop, 10);
56494         var cleft = parseInt(el.offsetLeft, 10);
56495         var cbot = ctop + el.offsetHeight;
56496         var cright = cleft + el.offsetWidth;
56497         
56498         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56499         var stop = parseInt(c.scrollTop, 10);
56500         var sleft = parseInt(c.scrollLeft, 10);
56501         var sbot = stop + ch;
56502         var sright = sleft + c.clientWidth;
56503         /*
56504         Roo.log('GridView.ensureVisible:' +
56505                 ' ctop:' + ctop +
56506                 ' c.clientHeight:' + c.clientHeight +
56507                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56508                 ' stop:' + stop +
56509                 ' cbot:' + cbot +
56510                 ' sbot:' + sbot +
56511                 ' ch:' + ch  
56512                 );
56513         */
56514         if(ctop < stop){
56515              c.scrollTop = ctop;
56516             //Roo.log("set scrolltop to ctop DISABLE?");
56517         }else if(cbot > sbot){
56518             //Roo.log("set scrolltop to cbot-ch");
56519             c.scrollTop = cbot-ch;
56520         }
56521         
56522         if(hscroll !== false){
56523             if(cleft < sleft){
56524                 c.scrollLeft = cleft;
56525             }else if(cright > sright){
56526                 c.scrollLeft = cright-c.clientWidth;
56527             }
56528         }
56529          
56530         return el;
56531     },
56532
56533     updateColumns : function(){
56534         this.grid.stopEditing();
56535         var cm = this.grid.colModel, colIds = this.getColumnIds();
56536         //var totalWidth = cm.getTotalWidth();
56537         var pos = 0;
56538         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56539             //if(cm.isHidden(i)) continue;
56540             var w = cm.getColumnWidth(i);
56541             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56542             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56543         }
56544         this.updateSplitters();
56545     },
56546
56547     generateRules : function(cm){
56548         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56549         Roo.util.CSS.removeStyleSheet(rulesId);
56550         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56551             var cid = cm.getColumnId(i);
56552             var align = '';
56553             if(cm.config[i].align){
56554                 align = 'text-align:'+cm.config[i].align+';';
56555             }
56556             var hidden = '';
56557             if(cm.isHidden(i)){
56558                 hidden = 'display:none;';
56559             }
56560             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56561             ruleBuf.push(
56562                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56563                     this.hdSelector, cid, " {\n", align, width, "}\n",
56564                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56565                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56566         }
56567         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56568     },
56569
56570     updateSplitters : function(){
56571         var cm = this.cm, s = this.getSplitters();
56572         if(s){ // splitters not created yet
56573             var pos = 0, locked = true;
56574             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56575                 if(cm.isHidden(i)) {
56576                     continue;
56577                 }
56578                 var w = cm.getColumnWidth(i); // make sure it's a number
56579                 if(!cm.isLocked(i) && locked){
56580                     pos = 0;
56581                     locked = false;
56582                 }
56583                 pos += w;
56584                 s[i].style.left = (pos-this.splitOffset) + "px";
56585             }
56586         }
56587     },
56588
56589     handleHiddenChange : function(colModel, colIndex, hidden){
56590         if(hidden){
56591             this.hideColumn(colIndex);
56592         }else{
56593             this.unhideColumn(colIndex);
56594         }
56595     },
56596
56597     hideColumn : function(colIndex){
56598         var cid = this.getColumnId(colIndex);
56599         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56600         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56601         if(Roo.isSafari){
56602             this.updateHeaders();
56603         }
56604         this.updateSplitters();
56605         this.layout();
56606     },
56607
56608     unhideColumn : function(colIndex){
56609         var cid = this.getColumnId(colIndex);
56610         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56611         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56612
56613         if(Roo.isSafari){
56614             this.updateHeaders();
56615         }
56616         this.updateSplitters();
56617         this.layout();
56618     },
56619
56620     insertRows : function(dm, firstRow, lastRow, isUpdate){
56621         if(firstRow == 0 && lastRow == dm.getCount()-1){
56622             this.refresh();
56623         }else{
56624             if(!isUpdate){
56625                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56626             }
56627             var s = this.getScrollState();
56628             var markup = this.renderRows(firstRow, lastRow);
56629             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56630             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56631             this.restoreScroll(s);
56632             if(!isUpdate){
56633                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56634                 this.syncRowHeights(firstRow, lastRow);
56635                 this.stripeRows(firstRow);
56636                 this.layout();
56637             }
56638         }
56639     },
56640
56641     bufferRows : function(markup, target, index){
56642         var before = null, trows = target.rows, tbody = target.tBodies[0];
56643         if(index < trows.length){
56644             before = trows[index];
56645         }
56646         var b = document.createElement("div");
56647         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56648         var rows = b.firstChild.rows;
56649         for(var i = 0, len = rows.length; i < len; i++){
56650             if(before){
56651                 tbody.insertBefore(rows[0], before);
56652             }else{
56653                 tbody.appendChild(rows[0]);
56654             }
56655         }
56656         b.innerHTML = "";
56657         b = null;
56658     },
56659
56660     deleteRows : function(dm, firstRow, lastRow){
56661         if(dm.getRowCount()<1){
56662             this.fireEvent("beforerefresh", this);
56663             this.mainBody.update("");
56664             this.lockedBody.update("");
56665             this.fireEvent("refresh", this);
56666         }else{
56667             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56668             var bt = this.getBodyTable();
56669             var tbody = bt.firstChild;
56670             var rows = bt.rows;
56671             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56672                 tbody.removeChild(rows[firstRow]);
56673             }
56674             this.stripeRows(firstRow);
56675             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56676         }
56677     },
56678
56679     updateRows : function(dataSource, firstRow, lastRow){
56680         var s = this.getScrollState();
56681         this.refresh();
56682         this.restoreScroll(s);
56683     },
56684
56685     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56686         if(!noRefresh){
56687            this.refresh();
56688         }
56689         this.updateHeaderSortState();
56690     },
56691
56692     getScrollState : function(){
56693         
56694         var sb = this.scroller.dom;
56695         return {left: sb.scrollLeft, top: sb.scrollTop};
56696     },
56697
56698     stripeRows : function(startRow){
56699         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56700             return;
56701         }
56702         startRow = startRow || 0;
56703         var rows = this.getBodyTable().rows;
56704         var lrows = this.getLockedTable().rows;
56705         var cls = ' x-grid-row-alt ';
56706         for(var i = startRow, len = rows.length; i < len; i++){
56707             var row = rows[i], lrow = lrows[i];
56708             var isAlt = ((i+1) % 2 == 0);
56709             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56710             if(isAlt == hasAlt){
56711                 continue;
56712             }
56713             if(isAlt){
56714                 row.className += " x-grid-row-alt";
56715             }else{
56716                 row.className = row.className.replace("x-grid-row-alt", "");
56717             }
56718             if(lrow){
56719                 lrow.className = row.className;
56720             }
56721         }
56722     },
56723
56724     restoreScroll : function(state){
56725         //Roo.log('GridView.restoreScroll');
56726         var sb = this.scroller.dom;
56727         sb.scrollLeft = state.left;
56728         sb.scrollTop = state.top;
56729         this.syncScroll();
56730     },
56731
56732     syncScroll : function(){
56733         //Roo.log('GridView.syncScroll');
56734         var sb = this.scroller.dom;
56735         var sh = this.mainHd.dom;
56736         var bs = this.mainBody.dom;
56737         var lv = this.lockedBody.dom;
56738         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56739         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56740     },
56741
56742     handleScroll : function(e){
56743         this.syncScroll();
56744         var sb = this.scroller.dom;
56745         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56746         e.stopEvent();
56747     },
56748
56749     handleWheel : function(e){
56750         var d = e.getWheelDelta();
56751         this.scroller.dom.scrollTop -= d*22;
56752         // set this here to prevent jumpy scrolling on large tables
56753         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56754         e.stopEvent();
56755     },
56756
56757     renderRows : function(startRow, endRow){
56758         // pull in all the crap needed to render rows
56759         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56760         var colCount = cm.getColumnCount();
56761
56762         if(ds.getCount() < 1){
56763             return ["", ""];
56764         }
56765
56766         // build a map for all the columns
56767         var cs = [];
56768         for(var i = 0; i < colCount; i++){
56769             var name = cm.getDataIndex(i);
56770             cs[i] = {
56771                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56772                 renderer : cm.getRenderer(i),
56773                 id : cm.getColumnId(i),
56774                 locked : cm.isLocked(i),
56775                 has_editor : cm.isCellEditable(i)
56776             };
56777         }
56778
56779         startRow = startRow || 0;
56780         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56781
56782         // records to render
56783         var rs = ds.getRange(startRow, endRow);
56784
56785         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56786     },
56787
56788     // As much as I hate to duplicate code, this was branched because FireFox really hates
56789     // [].join("") on strings. The performance difference was substantial enough to
56790     // branch this function
56791     doRender : Roo.isGecko ?
56792             function(cs, rs, ds, startRow, colCount, stripe){
56793                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56794                 // buffers
56795                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56796                 
56797                 var hasListener = this.grid.hasListener('rowclass');
56798                 var rowcfg = {};
56799                 for(var j = 0, len = rs.length; j < len; j++){
56800                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56801                     for(var i = 0; i < colCount; i++){
56802                         c = cs[i];
56803                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56804                         p.id = c.id;
56805                         p.css = p.attr = "";
56806                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56807                         if(p.value == undefined || p.value === "") {
56808                             p.value = "&#160;";
56809                         }
56810                         if(c.has_editor){
56811                             p.css += ' x-grid-editable-cell';
56812                         }
56813                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56814                             p.css +=  ' x-grid-dirty-cell';
56815                         }
56816                         var markup = ct.apply(p);
56817                         if(!c.locked){
56818                             cb+= markup;
56819                         }else{
56820                             lcb+= markup;
56821                         }
56822                     }
56823                     var alt = [];
56824                     if(stripe && ((rowIndex+1) % 2 == 0)){
56825                         alt.push("x-grid-row-alt")
56826                     }
56827                     if(r.dirty){
56828                         alt.push(  " x-grid-dirty-row");
56829                     }
56830                     rp.cells = lcb;
56831                     if(this.getRowClass){
56832                         alt.push(this.getRowClass(r, rowIndex));
56833                     }
56834                     if (hasListener) {
56835                         rowcfg = {
56836                              
56837                             record: r,
56838                             rowIndex : rowIndex,
56839                             rowClass : ''
56840                         };
56841                         this.grid.fireEvent('rowclass', this, rowcfg);
56842                         alt.push(rowcfg.rowClass);
56843                     }
56844                     rp.alt = alt.join(" ");
56845                     lbuf+= rt.apply(rp);
56846                     rp.cells = cb;
56847                     buf+=  rt.apply(rp);
56848                 }
56849                 return [lbuf, buf];
56850             } :
56851             function(cs, rs, ds, startRow, colCount, stripe){
56852                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56853                 // buffers
56854                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56855                 var hasListener = this.grid.hasListener('rowclass');
56856  
56857                 var rowcfg = {};
56858                 for(var j = 0, len = rs.length; j < len; j++){
56859                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56860                     for(var i = 0; i < colCount; i++){
56861                         c = cs[i];
56862                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56863                         p.id = c.id;
56864                         p.css = p.attr = "";
56865                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56866                         if(p.value == undefined || p.value === "") {
56867                             p.value = "&#160;";
56868                         }
56869                         //Roo.log(c);
56870                          if(c.has_editor){
56871                             p.css += ' x-grid-editable-cell';
56872                         }
56873                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56874                             p.css += ' x-grid-dirty-cell' 
56875                         }
56876                         
56877                         var markup = ct.apply(p);
56878                         if(!c.locked){
56879                             cb[cb.length] = markup;
56880                         }else{
56881                             lcb[lcb.length] = markup;
56882                         }
56883                     }
56884                     var alt = [];
56885                     if(stripe && ((rowIndex+1) % 2 == 0)){
56886                         alt.push( "x-grid-row-alt");
56887                     }
56888                     if(r.dirty){
56889                         alt.push(" x-grid-dirty-row");
56890                     }
56891                     rp.cells = lcb;
56892                     if(this.getRowClass){
56893                         alt.push( this.getRowClass(r, rowIndex));
56894                     }
56895                     if (hasListener) {
56896                         rowcfg = {
56897                              
56898                             record: r,
56899                             rowIndex : rowIndex,
56900                             rowClass : ''
56901                         };
56902                         this.grid.fireEvent('rowclass', this, rowcfg);
56903                         alt.push(rowcfg.rowClass);
56904                     }
56905                     
56906                     rp.alt = alt.join(" ");
56907                     rp.cells = lcb.join("");
56908                     lbuf[lbuf.length] = rt.apply(rp);
56909                     rp.cells = cb.join("");
56910                     buf[buf.length] =  rt.apply(rp);
56911                 }
56912                 return [lbuf.join(""), buf.join("")];
56913             },
56914
56915     renderBody : function(){
56916         var markup = this.renderRows();
56917         var bt = this.templates.body;
56918         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56919     },
56920
56921     /**
56922      * Refreshes the grid
56923      * @param {Boolean} headersToo
56924      */
56925     refresh : function(headersToo){
56926         this.fireEvent("beforerefresh", this);
56927         this.grid.stopEditing();
56928         var result = this.renderBody();
56929         this.lockedBody.update(result[0]);
56930         this.mainBody.update(result[1]);
56931         if(headersToo === true){
56932             this.updateHeaders();
56933             this.updateColumns();
56934             this.updateSplitters();
56935             this.updateHeaderSortState();
56936         }
56937         this.syncRowHeights();
56938         this.layout();
56939         this.fireEvent("refresh", this);
56940     },
56941
56942     handleColumnMove : function(cm, oldIndex, newIndex){
56943         this.indexMap = null;
56944         var s = this.getScrollState();
56945         this.refresh(true);
56946         this.restoreScroll(s);
56947         this.afterMove(newIndex);
56948     },
56949
56950     afterMove : function(colIndex){
56951         if(this.enableMoveAnim && Roo.enableFx){
56952             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56953         }
56954         // if multisort - fix sortOrder, and reload..
56955         if (this.grid.dataSource.multiSort) {
56956             // the we can call sort again..
56957             var dm = this.grid.dataSource;
56958             var cm = this.grid.colModel;
56959             var so = [];
56960             for(var i = 0; i < cm.config.length; i++ ) {
56961                 
56962                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56963                     continue; // dont' bother, it's not in sort list or being set.
56964                 }
56965                 
56966                 so.push(cm.config[i].dataIndex);
56967             };
56968             dm.sortOrder = so;
56969             dm.load(dm.lastOptions);
56970             
56971             
56972         }
56973         
56974     },
56975
56976     updateCell : function(dm, rowIndex, dataIndex){
56977         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56978         if(typeof colIndex == "undefined"){ // not present in grid
56979             return;
56980         }
56981         var cm = this.grid.colModel;
56982         var cell = this.getCell(rowIndex, colIndex);
56983         var cellText = this.getCellText(rowIndex, colIndex);
56984
56985         var p = {
56986             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56987             id : cm.getColumnId(colIndex),
56988             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56989         };
56990         var renderer = cm.getRenderer(colIndex);
56991         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56992         if(typeof val == "undefined" || val === "") {
56993             val = "&#160;";
56994         }
56995         cellText.innerHTML = val;
56996         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56997         this.syncRowHeights(rowIndex, rowIndex);
56998     },
56999
57000     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57001         var maxWidth = 0;
57002         if(this.grid.autoSizeHeaders){
57003             var h = this.getHeaderCellMeasure(colIndex);
57004             maxWidth = Math.max(maxWidth, h.scrollWidth);
57005         }
57006         var tb, index;
57007         if(this.cm.isLocked(colIndex)){
57008             tb = this.getLockedTable();
57009             index = colIndex;
57010         }else{
57011             tb = this.getBodyTable();
57012             index = colIndex - this.cm.getLockedCount();
57013         }
57014         if(tb && tb.rows){
57015             var rows = tb.rows;
57016             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57017             for(var i = 0; i < stopIndex; i++){
57018                 var cell = rows[i].childNodes[index].firstChild;
57019                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57020             }
57021         }
57022         return maxWidth + /*margin for error in IE*/ 5;
57023     },
57024     /**
57025      * Autofit a column to its content.
57026      * @param {Number} colIndex
57027      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57028      */
57029      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57030          if(this.cm.isHidden(colIndex)){
57031              return; // can't calc a hidden column
57032          }
57033         if(forceMinSize){
57034             var cid = this.cm.getColumnId(colIndex);
57035             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57036            if(this.grid.autoSizeHeaders){
57037                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57038            }
57039         }
57040         var newWidth = this.calcColumnWidth(colIndex);
57041         this.cm.setColumnWidth(colIndex,
57042             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57043         if(!suppressEvent){
57044             this.grid.fireEvent("columnresize", colIndex, newWidth);
57045         }
57046     },
57047
57048     /**
57049      * Autofits all columns to their content and then expands to fit any extra space in the grid
57050      */
57051      autoSizeColumns : function(){
57052         var cm = this.grid.colModel;
57053         var colCount = cm.getColumnCount();
57054         for(var i = 0; i < colCount; i++){
57055             this.autoSizeColumn(i, true, true);
57056         }
57057         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57058             this.fitColumns();
57059         }else{
57060             this.updateColumns();
57061             this.layout();
57062         }
57063     },
57064
57065     /**
57066      * Autofits all columns to the grid's width proportionate with their current size
57067      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57068      */
57069     fitColumns : function(reserveScrollSpace){
57070         var cm = this.grid.colModel;
57071         var colCount = cm.getColumnCount();
57072         var cols = [];
57073         var width = 0;
57074         var i, w;
57075         for (i = 0; i < colCount; i++){
57076             if(!cm.isHidden(i) && !cm.isFixed(i)){
57077                 w = cm.getColumnWidth(i);
57078                 cols.push(i);
57079                 cols.push(w);
57080                 width += w;
57081             }
57082         }
57083         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57084         if(reserveScrollSpace){
57085             avail -= 17;
57086         }
57087         var frac = (avail - cm.getTotalWidth())/width;
57088         while (cols.length){
57089             w = cols.pop();
57090             i = cols.pop();
57091             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57092         }
57093         this.updateColumns();
57094         this.layout();
57095     },
57096
57097     onRowSelect : function(rowIndex){
57098         var row = this.getRowComposite(rowIndex);
57099         row.addClass("x-grid-row-selected");
57100     },
57101
57102     onRowDeselect : function(rowIndex){
57103         var row = this.getRowComposite(rowIndex);
57104         row.removeClass("x-grid-row-selected");
57105     },
57106
57107     onCellSelect : function(row, col){
57108         var cell = this.getCell(row, col);
57109         if(cell){
57110             Roo.fly(cell).addClass("x-grid-cell-selected");
57111         }
57112     },
57113
57114     onCellDeselect : function(row, col){
57115         var cell = this.getCell(row, col);
57116         if(cell){
57117             Roo.fly(cell).removeClass("x-grid-cell-selected");
57118         }
57119     },
57120
57121     updateHeaderSortState : function(){
57122         
57123         // sort state can be single { field: xxx, direction : yyy}
57124         // or   { xxx=>ASC , yyy : DESC ..... }
57125         
57126         var mstate = {};
57127         if (!this.ds.multiSort) { 
57128             var state = this.ds.getSortState();
57129             if(!state){
57130                 return;
57131             }
57132             mstate[state.field] = state.direction;
57133             // FIXME... - this is not used here.. but might be elsewhere..
57134             this.sortState = state;
57135             
57136         } else {
57137             mstate = this.ds.sortToggle;
57138         }
57139         //remove existing sort classes..
57140         
57141         var sc = this.sortClasses;
57142         var hds = this.el.select(this.headerSelector).removeClass(sc);
57143         
57144         for(var f in mstate) {
57145         
57146             var sortColumn = this.cm.findColumnIndex(f);
57147             
57148             if(sortColumn != -1){
57149                 var sortDir = mstate[f];        
57150                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57151             }
57152         }
57153         
57154          
57155         
57156     },
57157
57158
57159     handleHeaderClick : function(g, index,e){
57160         
57161         Roo.log("header click");
57162         
57163         if (Roo.isTouch) {
57164             // touch events on header are handled by context
57165             this.handleHdCtx(g,index,e);
57166             return;
57167         }
57168         
57169         
57170         if(this.headersDisabled){
57171             return;
57172         }
57173         var dm = g.dataSource, cm = g.colModel;
57174         if(!cm.isSortable(index)){
57175             return;
57176         }
57177         g.stopEditing();
57178         
57179         if (dm.multiSort) {
57180             // update the sortOrder
57181             var so = [];
57182             for(var i = 0; i < cm.config.length; i++ ) {
57183                 
57184                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57185                     continue; // dont' bother, it's not in sort list or being set.
57186                 }
57187                 
57188                 so.push(cm.config[i].dataIndex);
57189             };
57190             dm.sortOrder = so;
57191         }
57192         
57193         
57194         dm.sort(cm.getDataIndex(index));
57195     },
57196
57197
57198     destroy : function(){
57199         if(this.colMenu){
57200             this.colMenu.removeAll();
57201             Roo.menu.MenuMgr.unregister(this.colMenu);
57202             this.colMenu.getEl().remove();
57203             delete this.colMenu;
57204         }
57205         if(this.hmenu){
57206             this.hmenu.removeAll();
57207             Roo.menu.MenuMgr.unregister(this.hmenu);
57208             this.hmenu.getEl().remove();
57209             delete this.hmenu;
57210         }
57211         if(this.grid.enableColumnMove){
57212             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57213             if(dds){
57214                 for(var dd in dds){
57215                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57216                         var elid = dds[dd].dragElId;
57217                         dds[dd].unreg();
57218                         Roo.get(elid).remove();
57219                     } else if(dds[dd].config.isTarget){
57220                         dds[dd].proxyTop.remove();
57221                         dds[dd].proxyBottom.remove();
57222                         dds[dd].unreg();
57223                     }
57224                     if(Roo.dd.DDM.locationCache[dd]){
57225                         delete Roo.dd.DDM.locationCache[dd];
57226                     }
57227                 }
57228                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57229             }
57230         }
57231         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57232         this.bind(null, null);
57233         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57234     },
57235
57236     handleLockChange : function(){
57237         this.refresh(true);
57238     },
57239
57240     onDenyColumnLock : function(){
57241
57242     },
57243
57244     onDenyColumnHide : function(){
57245
57246     },
57247
57248     handleHdMenuClick : function(item){
57249         var index = this.hdCtxIndex;
57250         var cm = this.cm, ds = this.ds;
57251         switch(item.id){
57252             case "asc":
57253                 ds.sort(cm.getDataIndex(index), "ASC");
57254                 break;
57255             case "desc":
57256                 ds.sort(cm.getDataIndex(index), "DESC");
57257                 break;
57258             case "lock":
57259                 var lc = cm.getLockedCount();
57260                 if(cm.getColumnCount(true) <= lc+1){
57261                     this.onDenyColumnLock();
57262                     return;
57263                 }
57264                 if(lc != index){
57265                     cm.setLocked(index, true, true);
57266                     cm.moveColumn(index, lc);
57267                     this.grid.fireEvent("columnmove", index, lc);
57268                 }else{
57269                     cm.setLocked(index, true);
57270                 }
57271             break;
57272             case "unlock":
57273                 var lc = cm.getLockedCount();
57274                 if((lc-1) != index){
57275                     cm.setLocked(index, false, true);
57276                     cm.moveColumn(index, lc-1);
57277                     this.grid.fireEvent("columnmove", index, lc-1);
57278                 }else{
57279                     cm.setLocked(index, false);
57280                 }
57281             break;
57282             case 'wider': // used to expand cols on touch..
57283             case 'narrow':
57284                 var cw = cm.getColumnWidth(index);
57285                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57286                 cw = Math.max(0, cw);
57287                 cw = Math.min(cw,4000);
57288                 cm.setColumnWidth(index, cw);
57289                 break;
57290                 
57291             default:
57292                 index = cm.getIndexById(item.id.substr(4));
57293                 if(index != -1){
57294                     if(item.checked && cm.getColumnCount(true) <= 1){
57295                         this.onDenyColumnHide();
57296                         return false;
57297                     }
57298                     cm.setHidden(index, item.checked);
57299                 }
57300         }
57301         return true;
57302     },
57303
57304     beforeColMenuShow : function(){
57305         var cm = this.cm,  colCount = cm.getColumnCount();
57306         this.colMenu.removeAll();
57307         for(var i = 0; i < colCount; i++){
57308             this.colMenu.add(new Roo.menu.CheckItem({
57309                 id: "col-"+cm.getColumnId(i),
57310                 text: cm.getColumnHeader(i),
57311                 checked: !cm.isHidden(i),
57312                 hideOnClick:false
57313             }));
57314         }
57315     },
57316
57317     handleHdCtx : function(g, index, e){
57318         e.stopEvent();
57319         var hd = this.getHeaderCell(index);
57320         this.hdCtxIndex = index;
57321         var ms = this.hmenu.items, cm = this.cm;
57322         ms.get("asc").setDisabled(!cm.isSortable(index));
57323         ms.get("desc").setDisabled(!cm.isSortable(index));
57324         if(this.grid.enableColLock !== false){
57325             ms.get("lock").setDisabled(cm.isLocked(index));
57326             ms.get("unlock").setDisabled(!cm.isLocked(index));
57327         }
57328         this.hmenu.show(hd, "tl-bl");
57329     },
57330
57331     handleHdOver : function(e){
57332         var hd = this.findHeaderCell(e.getTarget());
57333         if(hd && !this.headersDisabled){
57334             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57335                this.fly(hd).addClass("x-grid-hd-over");
57336             }
57337         }
57338     },
57339
57340     handleHdOut : function(e){
57341         var hd = this.findHeaderCell(e.getTarget());
57342         if(hd){
57343             this.fly(hd).removeClass("x-grid-hd-over");
57344         }
57345     },
57346
57347     handleSplitDblClick : function(e, t){
57348         var i = this.getCellIndex(t);
57349         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57350             this.autoSizeColumn(i, true);
57351             this.layout();
57352         }
57353     },
57354
57355     render : function(){
57356
57357         var cm = this.cm;
57358         var colCount = cm.getColumnCount();
57359
57360         if(this.grid.monitorWindowResize === true){
57361             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57362         }
57363         var header = this.renderHeaders();
57364         var body = this.templates.body.apply({rows:""});
57365         var html = this.templates.master.apply({
57366             lockedBody: body,
57367             body: body,
57368             lockedHeader: header[0],
57369             header: header[1]
57370         });
57371
57372         //this.updateColumns();
57373
57374         this.grid.getGridEl().dom.innerHTML = html;
57375
57376         this.initElements();
57377         
57378         // a kludge to fix the random scolling effect in webkit
57379         this.el.on("scroll", function() {
57380             this.el.dom.scrollTop=0; // hopefully not recursive..
57381         },this);
57382
57383         this.scroller.on("scroll", this.handleScroll, this);
57384         this.lockedBody.on("mousewheel", this.handleWheel, this);
57385         this.mainBody.on("mousewheel", this.handleWheel, this);
57386
57387         this.mainHd.on("mouseover", this.handleHdOver, this);
57388         this.mainHd.on("mouseout", this.handleHdOut, this);
57389         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57390                 {delegate: "."+this.splitClass});
57391
57392         this.lockedHd.on("mouseover", this.handleHdOver, this);
57393         this.lockedHd.on("mouseout", this.handleHdOut, this);
57394         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57395                 {delegate: "."+this.splitClass});
57396
57397         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57398             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57399         }
57400
57401         this.updateSplitters();
57402
57403         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57404             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57405             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57406         }
57407
57408         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57409             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57410             this.hmenu.add(
57411                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57412                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57413             );
57414             if(this.grid.enableColLock !== false){
57415                 this.hmenu.add('-',
57416                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57417                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57418                 );
57419             }
57420             if (Roo.isTouch) {
57421                  this.hmenu.add('-',
57422                     {id:"wider", text: this.columnsWiderText},
57423                     {id:"narrow", text: this.columnsNarrowText }
57424                 );
57425                 
57426                  
57427             }
57428             
57429             if(this.grid.enableColumnHide !== false){
57430
57431                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57432                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57433                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57434
57435                 this.hmenu.add('-',
57436                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57437                 );
57438             }
57439             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57440
57441             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57442         }
57443
57444         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57445             this.dd = new Roo.grid.GridDragZone(this.grid, {
57446                 ddGroup : this.grid.ddGroup || 'GridDD'
57447             });
57448             
57449         }
57450
57451         /*
57452         for(var i = 0; i < colCount; i++){
57453             if(cm.isHidden(i)){
57454                 this.hideColumn(i);
57455             }
57456             if(cm.config[i].align){
57457                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57458                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57459             }
57460         }*/
57461         
57462         this.updateHeaderSortState();
57463
57464         this.beforeInitialResize();
57465         this.layout(true);
57466
57467         // two part rendering gives faster view to the user
57468         this.renderPhase2.defer(1, this);
57469     },
57470
57471     renderPhase2 : function(){
57472         // render the rows now
57473         this.refresh();
57474         if(this.grid.autoSizeColumns){
57475             this.autoSizeColumns();
57476         }
57477     },
57478
57479     beforeInitialResize : function(){
57480
57481     },
57482
57483     onColumnSplitterMoved : function(i, w){
57484         this.userResized = true;
57485         var cm = this.grid.colModel;
57486         cm.setColumnWidth(i, w, true);
57487         var cid = cm.getColumnId(i);
57488         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57489         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57490         this.updateSplitters();
57491         this.layout();
57492         this.grid.fireEvent("columnresize", i, w);
57493     },
57494
57495     syncRowHeights : function(startIndex, endIndex){
57496         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57497             startIndex = startIndex || 0;
57498             var mrows = this.getBodyTable().rows;
57499             var lrows = this.getLockedTable().rows;
57500             var len = mrows.length-1;
57501             endIndex = Math.min(endIndex || len, len);
57502             for(var i = startIndex; i <= endIndex; i++){
57503                 var m = mrows[i], l = lrows[i];
57504                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57505                 m.style.height = l.style.height = h + "px";
57506             }
57507         }
57508     },
57509
57510     layout : function(initialRender, is2ndPass){
57511         var g = this.grid;
57512         var auto = g.autoHeight;
57513         var scrollOffset = 16;
57514         var c = g.getGridEl(), cm = this.cm,
57515                 expandCol = g.autoExpandColumn,
57516                 gv = this;
57517         //c.beginMeasure();
57518
57519         if(!c.dom.offsetWidth){ // display:none?
57520             if(initialRender){
57521                 this.lockedWrap.show();
57522                 this.mainWrap.show();
57523             }
57524             return;
57525         }
57526
57527         var hasLock = this.cm.isLocked(0);
57528
57529         var tbh = this.headerPanel.getHeight();
57530         var bbh = this.footerPanel.getHeight();
57531
57532         if(auto){
57533             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57534             var newHeight = ch + c.getBorderWidth("tb");
57535             if(g.maxHeight){
57536                 newHeight = Math.min(g.maxHeight, newHeight);
57537             }
57538             c.setHeight(newHeight);
57539         }
57540
57541         if(g.autoWidth){
57542             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57543         }
57544
57545         var s = this.scroller;
57546
57547         var csize = c.getSize(true);
57548
57549         this.el.setSize(csize.width, csize.height);
57550
57551         this.headerPanel.setWidth(csize.width);
57552         this.footerPanel.setWidth(csize.width);
57553
57554         var hdHeight = this.mainHd.getHeight();
57555         var vw = csize.width;
57556         var vh = csize.height - (tbh + bbh);
57557
57558         s.setSize(vw, vh);
57559
57560         var bt = this.getBodyTable();
57561         
57562         if(cm.getLockedCount() == cm.config.length){
57563             bt = this.getLockedTable();
57564         }
57565         
57566         var ltWidth = hasLock ?
57567                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57568
57569         var scrollHeight = bt.offsetHeight;
57570         var scrollWidth = ltWidth + bt.offsetWidth;
57571         var vscroll = false, hscroll = false;
57572
57573         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57574
57575         var lw = this.lockedWrap, mw = this.mainWrap;
57576         var lb = this.lockedBody, mb = this.mainBody;
57577
57578         setTimeout(function(){
57579             var t = s.dom.offsetTop;
57580             var w = s.dom.clientWidth,
57581                 h = s.dom.clientHeight;
57582
57583             lw.setTop(t);
57584             lw.setSize(ltWidth, h);
57585
57586             mw.setLeftTop(ltWidth, t);
57587             mw.setSize(w-ltWidth, h);
57588
57589             lb.setHeight(h-hdHeight);
57590             mb.setHeight(h-hdHeight);
57591
57592             if(is2ndPass !== true && !gv.userResized && expandCol){
57593                 // high speed resize without full column calculation
57594                 
57595                 var ci = cm.getIndexById(expandCol);
57596                 if (ci < 0) {
57597                     ci = cm.findColumnIndex(expandCol);
57598                 }
57599                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57600                 var expandId = cm.getColumnId(ci);
57601                 var  tw = cm.getTotalWidth(false);
57602                 var currentWidth = cm.getColumnWidth(ci);
57603                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57604                 if(currentWidth != cw){
57605                     cm.setColumnWidth(ci, cw, true);
57606                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57607                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57608                     gv.updateSplitters();
57609                     gv.layout(false, true);
57610                 }
57611             }
57612
57613             if(initialRender){
57614                 lw.show();
57615                 mw.show();
57616             }
57617             //c.endMeasure();
57618         }, 10);
57619     },
57620
57621     onWindowResize : function(){
57622         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57623             return;
57624         }
57625         this.layout();
57626     },
57627
57628     appendFooter : function(parentEl){
57629         return null;
57630     },
57631
57632     sortAscText : "Sort Ascending",
57633     sortDescText : "Sort Descending",
57634     lockText : "Lock Column",
57635     unlockText : "Unlock Column",
57636     columnsText : "Columns",
57637  
57638     columnsWiderText : "Wider",
57639     columnsNarrowText : "Thinner"
57640 });
57641
57642
57643 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57644     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57645     this.proxy.el.addClass('x-grid3-col-dd');
57646 };
57647
57648 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57649     handleMouseDown : function(e){
57650
57651     },
57652
57653     callHandleMouseDown : function(e){
57654         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57655     }
57656 });
57657 /*
57658  * Based on:
57659  * Ext JS Library 1.1.1
57660  * Copyright(c) 2006-2007, Ext JS, LLC.
57661  *
57662  * Originally Released Under LGPL - original licence link has changed is not relivant.
57663  *
57664  * Fork - LGPL
57665  * <script type="text/javascript">
57666  */
57667  
57668 // private
57669 // This is a support class used internally by the Grid components
57670 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57671     this.grid = grid;
57672     this.view = grid.getView();
57673     this.proxy = this.view.resizeProxy;
57674     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57675         "gridSplitters" + this.grid.getGridEl().id, {
57676         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57677     });
57678     this.setHandleElId(Roo.id(hd));
57679     this.setOuterHandleElId(Roo.id(hd2));
57680     this.scroll = false;
57681 };
57682 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57683     fly: Roo.Element.fly,
57684
57685     b4StartDrag : function(x, y){
57686         this.view.headersDisabled = true;
57687         this.proxy.setHeight(this.view.mainWrap.getHeight());
57688         var w = this.cm.getColumnWidth(this.cellIndex);
57689         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57690         this.resetConstraints();
57691         this.setXConstraint(minw, 1000);
57692         this.setYConstraint(0, 0);
57693         this.minX = x - minw;
57694         this.maxX = x + 1000;
57695         this.startPos = x;
57696         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57697     },
57698
57699
57700     handleMouseDown : function(e){
57701         ev = Roo.EventObject.setEvent(e);
57702         var t = this.fly(ev.getTarget());
57703         if(t.hasClass("x-grid-split")){
57704             this.cellIndex = this.view.getCellIndex(t.dom);
57705             this.split = t.dom;
57706             this.cm = this.grid.colModel;
57707             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57708                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57709             }
57710         }
57711     },
57712
57713     endDrag : function(e){
57714         this.view.headersDisabled = false;
57715         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57716         var diff = endX - this.startPos;
57717         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57718     },
57719
57720     autoOffset : function(){
57721         this.setDelta(0,0);
57722     }
57723 });/*
57724  * Based on:
57725  * Ext JS Library 1.1.1
57726  * Copyright(c) 2006-2007, Ext JS, LLC.
57727  *
57728  * Originally Released Under LGPL - original licence link has changed is not relivant.
57729  *
57730  * Fork - LGPL
57731  * <script type="text/javascript">
57732  */
57733  
57734 // private
57735 // This is a support class used internally by the Grid components
57736 Roo.grid.GridDragZone = function(grid, config){
57737     this.view = grid.getView();
57738     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57739     if(this.view.lockedBody){
57740         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57741         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57742     }
57743     this.scroll = false;
57744     this.grid = grid;
57745     this.ddel = document.createElement('div');
57746     this.ddel.className = 'x-grid-dd-wrap';
57747 };
57748
57749 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57750     ddGroup : "GridDD",
57751
57752     getDragData : function(e){
57753         var t = Roo.lib.Event.getTarget(e);
57754         var rowIndex = this.view.findRowIndex(t);
57755         var sm = this.grid.selModel;
57756             
57757         //Roo.log(rowIndex);
57758         
57759         if (sm.getSelectedCell) {
57760             // cell selection..
57761             if (!sm.getSelectedCell()) {
57762                 return false;
57763             }
57764             if (rowIndex != sm.getSelectedCell()[0]) {
57765                 return false;
57766             }
57767         
57768         }
57769         
57770         if(rowIndex !== false){
57771             
57772             // if editorgrid.. 
57773             
57774             
57775             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57776                
57777             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57778               //  
57779             //}
57780             if (e.hasModifier()){
57781                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57782             }
57783             
57784             Roo.log("getDragData");
57785             
57786             return {
57787                 grid: this.grid,
57788                 ddel: this.ddel,
57789                 rowIndex: rowIndex,
57790                 selections:sm.getSelections ? sm.getSelections() : (
57791                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57792                 )
57793             };
57794         }
57795         return false;
57796     },
57797
57798     onInitDrag : function(e){
57799         var data = this.dragData;
57800         this.ddel.innerHTML = this.grid.getDragDropText();
57801         this.proxy.update(this.ddel);
57802         // fire start drag?
57803     },
57804
57805     afterRepair : function(){
57806         this.dragging = false;
57807     },
57808
57809     getRepairXY : function(e, data){
57810         return false;
57811     },
57812
57813     onEndDrag : function(data, e){
57814         // fire end drag?
57815     },
57816
57817     onValidDrop : function(dd, e, id){
57818         // fire drag drop?
57819         this.hideProxy();
57820     },
57821
57822     beforeInvalidDrop : function(e, id){
57823
57824     }
57825 });/*
57826  * Based on:
57827  * Ext JS Library 1.1.1
57828  * Copyright(c) 2006-2007, Ext JS, LLC.
57829  *
57830  * Originally Released Under LGPL - original licence link has changed is not relivant.
57831  *
57832  * Fork - LGPL
57833  * <script type="text/javascript">
57834  */
57835  
57836
57837 /**
57838  * @class Roo.grid.ColumnModel
57839  * @extends Roo.util.Observable
57840  * This is the default implementation of a ColumnModel used by the Grid. It defines
57841  * the columns in the grid.
57842  * <br>Usage:<br>
57843  <pre><code>
57844  var colModel = new Roo.grid.ColumnModel([
57845         {header: "Ticker", width: 60, sortable: true, locked: true},
57846         {header: "Company Name", width: 150, sortable: true},
57847         {header: "Market Cap.", width: 100, sortable: true},
57848         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57849         {header: "Employees", width: 100, sortable: true, resizable: false}
57850  ]);
57851  </code></pre>
57852  * <p>
57853  
57854  * The config options listed for this class are options which may appear in each
57855  * individual column definition.
57856  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57857  * @constructor
57858  * @param {Object} config An Array of column config objects. See this class's
57859  * config objects for details.
57860 */
57861 Roo.grid.ColumnModel = function(config){
57862         /**
57863      * The config passed into the constructor
57864      */
57865     this.config = config;
57866     this.lookup = {};
57867
57868     // if no id, create one
57869     // if the column does not have a dataIndex mapping,
57870     // map it to the order it is in the config
57871     for(var i = 0, len = config.length; i < len; i++){
57872         var c = config[i];
57873         if(typeof c.dataIndex == "undefined"){
57874             c.dataIndex = i;
57875         }
57876         if(typeof c.renderer == "string"){
57877             c.renderer = Roo.util.Format[c.renderer];
57878         }
57879         if(typeof c.id == "undefined"){
57880             c.id = Roo.id();
57881         }
57882         if(c.editor && c.editor.xtype){
57883             c.editor  = Roo.factory(c.editor, Roo.grid);
57884         }
57885         if(c.editor && c.editor.isFormField){
57886             c.editor = new Roo.grid.GridEditor(c.editor);
57887         }
57888         this.lookup[c.id] = c;
57889     }
57890
57891     /**
57892      * The width of columns which have no width specified (defaults to 100)
57893      * @type Number
57894      */
57895     this.defaultWidth = 100;
57896
57897     /**
57898      * Default sortable of columns which have no sortable specified (defaults to false)
57899      * @type Boolean
57900      */
57901     this.defaultSortable = false;
57902
57903     this.addEvents({
57904         /**
57905              * @event widthchange
57906              * Fires when the width of a column changes.
57907              * @param {ColumnModel} this
57908              * @param {Number} columnIndex The column index
57909              * @param {Number} newWidth The new width
57910              */
57911             "widthchange": true,
57912         /**
57913              * @event headerchange
57914              * Fires when the text of a header changes.
57915              * @param {ColumnModel} this
57916              * @param {Number} columnIndex The column index
57917              * @param {Number} newText The new header text
57918              */
57919             "headerchange": true,
57920         /**
57921              * @event hiddenchange
57922              * Fires when a column is hidden or "unhidden".
57923              * @param {ColumnModel} this
57924              * @param {Number} columnIndex The column index
57925              * @param {Boolean} hidden true if hidden, false otherwise
57926              */
57927             "hiddenchange": true,
57928             /**
57929          * @event columnmoved
57930          * Fires when a column is moved.
57931          * @param {ColumnModel} this
57932          * @param {Number} oldIndex
57933          * @param {Number} newIndex
57934          */
57935         "columnmoved" : true,
57936         /**
57937          * @event columlockchange
57938          * Fires when a column's locked state is changed
57939          * @param {ColumnModel} this
57940          * @param {Number} colIndex
57941          * @param {Boolean} locked true if locked
57942          */
57943         "columnlockchange" : true
57944     });
57945     Roo.grid.ColumnModel.superclass.constructor.call(this);
57946 };
57947 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57948     /**
57949      * @cfg {String} header The header text to display in the Grid view.
57950      */
57951     /**
57952      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57953      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57954      * specified, the column's index is used as an index into the Record's data Array.
57955      */
57956     /**
57957      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57958      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57959      */
57960     /**
57961      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57962      * Defaults to the value of the {@link #defaultSortable} property.
57963      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57964      */
57965     /**
57966      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57967      */
57968     /**
57969      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57970      */
57971     /**
57972      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57973      */
57974     /**
57975      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57976      */
57977     /**
57978      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57979      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57980      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57981      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57982      */
57983        /**
57984      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57985      */
57986     /**
57987      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57988      */
57989     /**
57990      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57991      */
57992     /**
57993      * @cfg {String} cursor (Optional)
57994      */
57995     /**
57996      * @cfg {String} tooltip (Optional)
57997      */
57998     /**
57999      * @cfg {Number} xs (Optional)
58000      */
58001     /**
58002      * @cfg {Number} sm (Optional)
58003      */
58004     /**
58005      * @cfg {Number} md (Optional)
58006      */
58007     /**
58008      * @cfg {Number} lg (Optional)
58009      */
58010     /**
58011      * Returns the id of the column at the specified index.
58012      * @param {Number} index The column index
58013      * @return {String} the id
58014      */
58015     getColumnId : function(index){
58016         return this.config[index].id;
58017     },
58018
58019     /**
58020      * Returns the column for a specified id.
58021      * @param {String} id The column id
58022      * @return {Object} the column
58023      */
58024     getColumnById : function(id){
58025         return this.lookup[id];
58026     },
58027
58028     
58029     /**
58030      * Returns the column for a specified dataIndex.
58031      * @param {String} dataIndex The column dataIndex
58032      * @return {Object|Boolean} the column or false if not found
58033      */
58034     getColumnByDataIndex: function(dataIndex){
58035         var index = this.findColumnIndex(dataIndex);
58036         return index > -1 ? this.config[index] : false;
58037     },
58038     
58039     /**
58040      * Returns the index for a specified column id.
58041      * @param {String} id The column id
58042      * @return {Number} the index, or -1 if not found
58043      */
58044     getIndexById : function(id){
58045         for(var i = 0, len = this.config.length; i < len; i++){
58046             if(this.config[i].id == id){
58047                 return i;
58048             }
58049         }
58050         return -1;
58051     },
58052     
58053     /**
58054      * Returns the index for a specified column dataIndex.
58055      * @param {String} dataIndex The column dataIndex
58056      * @return {Number} the index, or -1 if not found
58057      */
58058     
58059     findColumnIndex : function(dataIndex){
58060         for(var i = 0, len = this.config.length; i < len; i++){
58061             if(this.config[i].dataIndex == dataIndex){
58062                 return i;
58063             }
58064         }
58065         return -1;
58066     },
58067     
58068     
58069     moveColumn : function(oldIndex, newIndex){
58070         var c = this.config[oldIndex];
58071         this.config.splice(oldIndex, 1);
58072         this.config.splice(newIndex, 0, c);
58073         this.dataMap = null;
58074         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58075     },
58076
58077     isLocked : function(colIndex){
58078         return this.config[colIndex].locked === true;
58079     },
58080
58081     setLocked : function(colIndex, value, suppressEvent){
58082         if(this.isLocked(colIndex) == value){
58083             return;
58084         }
58085         this.config[colIndex].locked = value;
58086         if(!suppressEvent){
58087             this.fireEvent("columnlockchange", this, colIndex, value);
58088         }
58089     },
58090
58091     getTotalLockedWidth : function(){
58092         var totalWidth = 0;
58093         for(var i = 0; i < this.config.length; i++){
58094             if(this.isLocked(i) && !this.isHidden(i)){
58095                 this.totalWidth += this.getColumnWidth(i);
58096             }
58097         }
58098         return totalWidth;
58099     },
58100
58101     getLockedCount : function(){
58102         for(var i = 0, len = this.config.length; i < len; i++){
58103             if(!this.isLocked(i)){
58104                 return i;
58105             }
58106         }
58107         
58108         return this.config.length;
58109     },
58110
58111     /**
58112      * Returns the number of columns.
58113      * @return {Number}
58114      */
58115     getColumnCount : function(visibleOnly){
58116         if(visibleOnly === true){
58117             var c = 0;
58118             for(var i = 0, len = this.config.length; i < len; i++){
58119                 if(!this.isHidden(i)){
58120                     c++;
58121                 }
58122             }
58123             return c;
58124         }
58125         return this.config.length;
58126     },
58127
58128     /**
58129      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58130      * @param {Function} fn
58131      * @param {Object} scope (optional)
58132      * @return {Array} result
58133      */
58134     getColumnsBy : function(fn, scope){
58135         var r = [];
58136         for(var i = 0, len = this.config.length; i < len; i++){
58137             var c = this.config[i];
58138             if(fn.call(scope||this, c, i) === true){
58139                 r[r.length] = c;
58140             }
58141         }
58142         return r;
58143     },
58144
58145     /**
58146      * Returns true if the specified column is sortable.
58147      * @param {Number} col The column index
58148      * @return {Boolean}
58149      */
58150     isSortable : function(col){
58151         if(typeof this.config[col].sortable == "undefined"){
58152             return this.defaultSortable;
58153         }
58154         return this.config[col].sortable;
58155     },
58156
58157     /**
58158      * Returns the rendering (formatting) function defined for the column.
58159      * @param {Number} col The column index.
58160      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58161      */
58162     getRenderer : function(col){
58163         if(!this.config[col].renderer){
58164             return Roo.grid.ColumnModel.defaultRenderer;
58165         }
58166         return this.config[col].renderer;
58167     },
58168
58169     /**
58170      * Sets the rendering (formatting) function for a column.
58171      * @param {Number} col The column index
58172      * @param {Function} fn The function to use to process the cell's raw data
58173      * to return HTML markup for the grid view. The render function is called with
58174      * the following parameters:<ul>
58175      * <li>Data value.</li>
58176      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58177      * <li>css A CSS style string to apply to the table cell.</li>
58178      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58179      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58180      * <li>Row index</li>
58181      * <li>Column index</li>
58182      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58183      */
58184     setRenderer : function(col, fn){
58185         this.config[col].renderer = fn;
58186     },
58187
58188     /**
58189      * Returns the width for the specified column.
58190      * @param {Number} col The column index
58191      * @return {Number}
58192      */
58193     getColumnWidth : function(col){
58194         return this.config[col].width * 1 || this.defaultWidth;
58195     },
58196
58197     /**
58198      * Sets the width for a column.
58199      * @param {Number} col The column index
58200      * @param {Number} width The new width
58201      */
58202     setColumnWidth : function(col, width, suppressEvent){
58203         this.config[col].width = width;
58204         this.totalWidth = null;
58205         if(!suppressEvent){
58206              this.fireEvent("widthchange", this, col, width);
58207         }
58208     },
58209
58210     /**
58211      * Returns the total width of all columns.
58212      * @param {Boolean} includeHidden True to include hidden column widths
58213      * @return {Number}
58214      */
58215     getTotalWidth : function(includeHidden){
58216         if(!this.totalWidth){
58217             this.totalWidth = 0;
58218             for(var i = 0, len = this.config.length; i < len; i++){
58219                 if(includeHidden || !this.isHidden(i)){
58220                     this.totalWidth += this.getColumnWidth(i);
58221                 }
58222             }
58223         }
58224         return this.totalWidth;
58225     },
58226
58227     /**
58228      * Returns the header for the specified column.
58229      * @param {Number} col The column index
58230      * @return {String}
58231      */
58232     getColumnHeader : function(col){
58233         return this.config[col].header;
58234     },
58235
58236     /**
58237      * Sets the header for a column.
58238      * @param {Number} col The column index
58239      * @param {String} header The new header
58240      */
58241     setColumnHeader : function(col, header){
58242         this.config[col].header = header;
58243         this.fireEvent("headerchange", this, col, header);
58244     },
58245
58246     /**
58247      * Returns the tooltip for the specified column.
58248      * @param {Number} col The column index
58249      * @return {String}
58250      */
58251     getColumnTooltip : function(col){
58252             return this.config[col].tooltip;
58253     },
58254     /**
58255      * Sets the tooltip for a column.
58256      * @param {Number} col The column index
58257      * @param {String} tooltip The new tooltip
58258      */
58259     setColumnTooltip : function(col, tooltip){
58260             this.config[col].tooltip = tooltip;
58261     },
58262
58263     /**
58264      * Returns the dataIndex for the specified column.
58265      * @param {Number} col The column index
58266      * @return {Number}
58267      */
58268     getDataIndex : function(col){
58269         return this.config[col].dataIndex;
58270     },
58271
58272     /**
58273      * Sets the dataIndex for a column.
58274      * @param {Number} col The column index
58275      * @param {Number} dataIndex The new dataIndex
58276      */
58277     setDataIndex : function(col, dataIndex){
58278         this.config[col].dataIndex = dataIndex;
58279     },
58280
58281     
58282     
58283     /**
58284      * Returns true if the cell is editable.
58285      * @param {Number} colIndex The column index
58286      * @param {Number} rowIndex The row index - this is nto actually used..?
58287      * @return {Boolean}
58288      */
58289     isCellEditable : function(colIndex, rowIndex){
58290         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58291     },
58292
58293     /**
58294      * Returns the editor defined for the cell/column.
58295      * return false or null to disable editing.
58296      * @param {Number} colIndex The column index
58297      * @param {Number} rowIndex The row index
58298      * @return {Object}
58299      */
58300     getCellEditor : function(colIndex, rowIndex){
58301         return this.config[colIndex].editor;
58302     },
58303
58304     /**
58305      * Sets if a column is editable.
58306      * @param {Number} col The column index
58307      * @param {Boolean} editable True if the column is editable
58308      */
58309     setEditable : function(col, editable){
58310         this.config[col].editable = editable;
58311     },
58312
58313
58314     /**
58315      * Returns true if the column is hidden.
58316      * @param {Number} colIndex The column index
58317      * @return {Boolean}
58318      */
58319     isHidden : function(colIndex){
58320         return this.config[colIndex].hidden;
58321     },
58322
58323
58324     /**
58325      * Returns true if the column width cannot be changed
58326      */
58327     isFixed : function(colIndex){
58328         return this.config[colIndex].fixed;
58329     },
58330
58331     /**
58332      * Returns true if the column can be resized
58333      * @return {Boolean}
58334      */
58335     isResizable : function(colIndex){
58336         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58337     },
58338     /**
58339      * Sets if a column is hidden.
58340      * @param {Number} colIndex The column index
58341      * @param {Boolean} hidden True if the column is hidden
58342      */
58343     setHidden : function(colIndex, hidden){
58344         this.config[colIndex].hidden = hidden;
58345         this.totalWidth = null;
58346         this.fireEvent("hiddenchange", this, colIndex, hidden);
58347     },
58348
58349     /**
58350      * Sets the editor for a column.
58351      * @param {Number} col The column index
58352      * @param {Object} editor The editor object
58353      */
58354     setEditor : function(col, editor){
58355         this.config[col].editor = editor;
58356     }
58357 });
58358
58359 Roo.grid.ColumnModel.defaultRenderer = function(value)
58360 {
58361     if(typeof value == "object") {
58362         return value;
58363     }
58364         if(typeof value == "string" && value.length < 1){
58365             return "&#160;";
58366         }
58367     
58368         return String.format("{0}", value);
58369 };
58370
58371 // Alias for backwards compatibility
58372 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58373 /*
58374  * Based on:
58375  * Ext JS Library 1.1.1
58376  * Copyright(c) 2006-2007, Ext JS, LLC.
58377  *
58378  * Originally Released Under LGPL - original licence link has changed is not relivant.
58379  *
58380  * Fork - LGPL
58381  * <script type="text/javascript">
58382  */
58383
58384 /**
58385  * @class Roo.grid.AbstractSelectionModel
58386  * @extends Roo.util.Observable
58387  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58388  * implemented by descendant classes.  This class should not be directly instantiated.
58389  * @constructor
58390  */
58391 Roo.grid.AbstractSelectionModel = function(){
58392     this.locked = false;
58393     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58394 };
58395
58396 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58397     /** @ignore Called by the grid automatically. Do not call directly. */
58398     init : function(grid){
58399         this.grid = grid;
58400         this.initEvents();
58401     },
58402
58403     /**
58404      * Locks the selections.
58405      */
58406     lock : function(){
58407         this.locked = true;
58408     },
58409
58410     /**
58411      * Unlocks the selections.
58412      */
58413     unlock : function(){
58414         this.locked = false;
58415     },
58416
58417     /**
58418      * Returns true if the selections are locked.
58419      * @return {Boolean}
58420      */
58421     isLocked : function(){
58422         return this.locked;
58423     }
58424 });/*
58425  * Based on:
58426  * Ext JS Library 1.1.1
58427  * Copyright(c) 2006-2007, Ext JS, LLC.
58428  *
58429  * Originally Released Under LGPL - original licence link has changed is not relivant.
58430  *
58431  * Fork - LGPL
58432  * <script type="text/javascript">
58433  */
58434 /**
58435  * @extends Roo.grid.AbstractSelectionModel
58436  * @class Roo.grid.RowSelectionModel
58437  * The default SelectionModel used by {@link Roo.grid.Grid}.
58438  * It supports multiple selections and keyboard selection/navigation. 
58439  * @constructor
58440  * @param {Object} config
58441  */
58442 Roo.grid.RowSelectionModel = function(config){
58443     Roo.apply(this, config);
58444     this.selections = new Roo.util.MixedCollection(false, function(o){
58445         return o.id;
58446     });
58447
58448     this.last = false;
58449     this.lastActive = false;
58450
58451     this.addEvents({
58452         /**
58453              * @event selectionchange
58454              * Fires when the selection changes
58455              * @param {SelectionModel} this
58456              */
58457             "selectionchange" : true,
58458         /**
58459              * @event afterselectionchange
58460              * Fires after the selection changes (eg. by key press or clicking)
58461              * @param {SelectionModel} this
58462              */
58463             "afterselectionchange" : true,
58464         /**
58465              * @event beforerowselect
58466              * Fires when a row is selected being selected, return false to cancel.
58467              * @param {SelectionModel} this
58468              * @param {Number} rowIndex The selected index
58469              * @param {Boolean} keepExisting False if other selections will be cleared
58470              */
58471             "beforerowselect" : true,
58472         /**
58473              * @event rowselect
58474              * Fires when a row is selected.
58475              * @param {SelectionModel} this
58476              * @param {Number} rowIndex The selected index
58477              * @param {Roo.data.Record} r The record
58478              */
58479             "rowselect" : true,
58480         /**
58481              * @event rowdeselect
58482              * Fires when a row is deselected.
58483              * @param {SelectionModel} this
58484              * @param {Number} rowIndex The selected index
58485              */
58486         "rowdeselect" : true
58487     });
58488     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58489     this.locked = false;
58490 };
58491
58492 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58493     /**
58494      * @cfg {Boolean} singleSelect
58495      * True to allow selection of only one row at a time (defaults to false)
58496      */
58497     singleSelect : false,
58498
58499     // private
58500     initEvents : function(){
58501
58502         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58503             this.grid.on("mousedown", this.handleMouseDown, this);
58504         }else{ // allow click to work like normal
58505             this.grid.on("rowclick", this.handleDragableRowClick, this);
58506         }
58507
58508         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58509             "up" : function(e){
58510                 if(!e.shiftKey){
58511                     this.selectPrevious(e.shiftKey);
58512                 }else if(this.last !== false && this.lastActive !== false){
58513                     var last = this.last;
58514                     this.selectRange(this.last,  this.lastActive-1);
58515                     this.grid.getView().focusRow(this.lastActive);
58516                     if(last !== false){
58517                         this.last = last;
58518                     }
58519                 }else{
58520                     this.selectFirstRow();
58521                 }
58522                 this.fireEvent("afterselectionchange", this);
58523             },
58524             "down" : function(e){
58525                 if(!e.shiftKey){
58526                     this.selectNext(e.shiftKey);
58527                 }else if(this.last !== false && this.lastActive !== false){
58528                     var last = this.last;
58529                     this.selectRange(this.last,  this.lastActive+1);
58530                     this.grid.getView().focusRow(this.lastActive);
58531                     if(last !== false){
58532                         this.last = last;
58533                     }
58534                 }else{
58535                     this.selectFirstRow();
58536                 }
58537                 this.fireEvent("afterselectionchange", this);
58538             },
58539             scope: this
58540         });
58541
58542         var view = this.grid.view;
58543         view.on("refresh", this.onRefresh, this);
58544         view.on("rowupdated", this.onRowUpdated, this);
58545         view.on("rowremoved", this.onRemove, this);
58546     },
58547
58548     // private
58549     onRefresh : function(){
58550         var ds = this.grid.dataSource, i, v = this.grid.view;
58551         var s = this.selections;
58552         s.each(function(r){
58553             if((i = ds.indexOfId(r.id)) != -1){
58554                 v.onRowSelect(i);
58555                 s.add(ds.getAt(i)); // updating the selection relate data
58556             }else{
58557                 s.remove(r);
58558             }
58559         });
58560     },
58561
58562     // private
58563     onRemove : function(v, index, r){
58564         this.selections.remove(r);
58565     },
58566
58567     // private
58568     onRowUpdated : function(v, index, r){
58569         if(this.isSelected(r)){
58570             v.onRowSelect(index);
58571         }
58572     },
58573
58574     /**
58575      * Select records.
58576      * @param {Array} records The records to select
58577      * @param {Boolean} keepExisting (optional) True to keep existing selections
58578      */
58579     selectRecords : function(records, keepExisting){
58580         if(!keepExisting){
58581             this.clearSelections();
58582         }
58583         var ds = this.grid.dataSource;
58584         for(var i = 0, len = records.length; i < len; i++){
58585             this.selectRow(ds.indexOf(records[i]), true);
58586         }
58587     },
58588
58589     /**
58590      * Gets the number of selected rows.
58591      * @return {Number}
58592      */
58593     getCount : function(){
58594         return this.selections.length;
58595     },
58596
58597     /**
58598      * Selects the first row in the grid.
58599      */
58600     selectFirstRow : function(){
58601         this.selectRow(0);
58602     },
58603
58604     /**
58605      * Select the last row.
58606      * @param {Boolean} keepExisting (optional) True to keep existing selections
58607      */
58608     selectLastRow : function(keepExisting){
58609         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58610     },
58611
58612     /**
58613      * Selects the row immediately following the last selected row.
58614      * @param {Boolean} keepExisting (optional) True to keep existing selections
58615      */
58616     selectNext : function(keepExisting){
58617         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58618             this.selectRow(this.last+1, keepExisting);
58619             this.grid.getView().focusRow(this.last);
58620         }
58621     },
58622
58623     /**
58624      * Selects the row that precedes the last selected row.
58625      * @param {Boolean} keepExisting (optional) True to keep existing selections
58626      */
58627     selectPrevious : function(keepExisting){
58628         if(this.last){
58629             this.selectRow(this.last-1, keepExisting);
58630             this.grid.getView().focusRow(this.last);
58631         }
58632     },
58633
58634     /**
58635      * Returns the selected records
58636      * @return {Array} Array of selected records
58637      */
58638     getSelections : function(){
58639         return [].concat(this.selections.items);
58640     },
58641
58642     /**
58643      * Returns the first selected record.
58644      * @return {Record}
58645      */
58646     getSelected : function(){
58647         return this.selections.itemAt(0);
58648     },
58649
58650
58651     /**
58652      * Clears all selections.
58653      */
58654     clearSelections : function(fast){
58655         if(this.locked) {
58656             return;
58657         }
58658         if(fast !== true){
58659             var ds = this.grid.dataSource;
58660             var s = this.selections;
58661             s.each(function(r){
58662                 this.deselectRow(ds.indexOfId(r.id));
58663             }, this);
58664             s.clear();
58665         }else{
58666             this.selections.clear();
58667         }
58668         this.last = false;
58669     },
58670
58671
58672     /**
58673      * Selects all rows.
58674      */
58675     selectAll : function(){
58676         if(this.locked) {
58677             return;
58678         }
58679         this.selections.clear();
58680         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58681             this.selectRow(i, true);
58682         }
58683     },
58684
58685     /**
58686      * Returns True if there is a selection.
58687      * @return {Boolean}
58688      */
58689     hasSelection : function(){
58690         return this.selections.length > 0;
58691     },
58692
58693     /**
58694      * Returns True if the specified row is selected.
58695      * @param {Number/Record} record The record or index of the record to check
58696      * @return {Boolean}
58697      */
58698     isSelected : function(index){
58699         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58700         return (r && this.selections.key(r.id) ? true : false);
58701     },
58702
58703     /**
58704      * Returns True if the specified record id is selected.
58705      * @param {String} id The id of record to check
58706      * @return {Boolean}
58707      */
58708     isIdSelected : function(id){
58709         return (this.selections.key(id) ? true : false);
58710     },
58711
58712     // private
58713     handleMouseDown : function(e, t){
58714         var view = this.grid.getView(), rowIndex;
58715         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58716             return;
58717         };
58718         if(e.shiftKey && this.last !== false){
58719             var last = this.last;
58720             this.selectRange(last, rowIndex, e.ctrlKey);
58721             this.last = last; // reset the last
58722             view.focusRow(rowIndex);
58723         }else{
58724             var isSelected = this.isSelected(rowIndex);
58725             if(e.button !== 0 && isSelected){
58726                 view.focusRow(rowIndex);
58727             }else if(e.ctrlKey && isSelected){
58728                 this.deselectRow(rowIndex);
58729             }else if(!isSelected){
58730                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58731                 view.focusRow(rowIndex);
58732             }
58733         }
58734         this.fireEvent("afterselectionchange", this);
58735     },
58736     // private
58737     handleDragableRowClick :  function(grid, rowIndex, e) 
58738     {
58739         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58740             this.selectRow(rowIndex, false);
58741             grid.view.focusRow(rowIndex);
58742              this.fireEvent("afterselectionchange", this);
58743         }
58744     },
58745     
58746     /**
58747      * Selects multiple rows.
58748      * @param {Array} rows Array of the indexes of the row to select
58749      * @param {Boolean} keepExisting (optional) True to keep existing selections
58750      */
58751     selectRows : function(rows, keepExisting){
58752         if(!keepExisting){
58753             this.clearSelections();
58754         }
58755         for(var i = 0, len = rows.length; i < len; i++){
58756             this.selectRow(rows[i], true);
58757         }
58758     },
58759
58760     /**
58761      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58762      * @param {Number} startRow The index of the first row in the range
58763      * @param {Number} endRow The index of the last row in the range
58764      * @param {Boolean} keepExisting (optional) True to retain existing selections
58765      */
58766     selectRange : function(startRow, endRow, keepExisting){
58767         if(this.locked) {
58768             return;
58769         }
58770         if(!keepExisting){
58771             this.clearSelections();
58772         }
58773         if(startRow <= endRow){
58774             for(var i = startRow; i <= endRow; i++){
58775                 this.selectRow(i, true);
58776             }
58777         }else{
58778             for(var i = startRow; i >= endRow; i--){
58779                 this.selectRow(i, true);
58780             }
58781         }
58782     },
58783
58784     /**
58785      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58786      * @param {Number} startRow The index of the first row in the range
58787      * @param {Number} endRow The index of the last row in the range
58788      */
58789     deselectRange : function(startRow, endRow, preventViewNotify){
58790         if(this.locked) {
58791             return;
58792         }
58793         for(var i = startRow; i <= endRow; i++){
58794             this.deselectRow(i, preventViewNotify);
58795         }
58796     },
58797
58798     /**
58799      * Selects a row.
58800      * @param {Number} row The index of the row to select
58801      * @param {Boolean} keepExisting (optional) True to keep existing selections
58802      */
58803     selectRow : function(index, keepExisting, preventViewNotify){
58804         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58805             return;
58806         }
58807         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58808             if(!keepExisting || this.singleSelect){
58809                 this.clearSelections();
58810             }
58811             var r = this.grid.dataSource.getAt(index);
58812             this.selections.add(r);
58813             this.last = this.lastActive = index;
58814             if(!preventViewNotify){
58815                 this.grid.getView().onRowSelect(index);
58816             }
58817             this.fireEvent("rowselect", this, index, r);
58818             this.fireEvent("selectionchange", this);
58819         }
58820     },
58821
58822     /**
58823      * Deselects a row.
58824      * @param {Number} row The index of the row to deselect
58825      */
58826     deselectRow : function(index, preventViewNotify){
58827         if(this.locked) {
58828             return;
58829         }
58830         if(this.last == index){
58831             this.last = false;
58832         }
58833         if(this.lastActive == index){
58834             this.lastActive = false;
58835         }
58836         var r = this.grid.dataSource.getAt(index);
58837         this.selections.remove(r);
58838         if(!preventViewNotify){
58839             this.grid.getView().onRowDeselect(index);
58840         }
58841         this.fireEvent("rowdeselect", this, index);
58842         this.fireEvent("selectionchange", this);
58843     },
58844
58845     // private
58846     restoreLast : function(){
58847         if(this._last){
58848             this.last = this._last;
58849         }
58850     },
58851
58852     // private
58853     acceptsNav : function(row, col, cm){
58854         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58855     },
58856
58857     // private
58858     onEditorKey : function(field, e){
58859         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58860         if(k == e.TAB){
58861             e.stopEvent();
58862             ed.completeEdit();
58863             if(e.shiftKey){
58864                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58865             }else{
58866                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58867             }
58868         }else if(k == e.ENTER && !e.ctrlKey){
58869             e.stopEvent();
58870             ed.completeEdit();
58871             if(e.shiftKey){
58872                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58873             }else{
58874                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58875             }
58876         }else if(k == e.ESC){
58877             ed.cancelEdit();
58878         }
58879         if(newCell){
58880             g.startEditing(newCell[0], newCell[1]);
58881         }
58882     }
58883 });/*
58884  * Based on:
58885  * Ext JS Library 1.1.1
58886  * Copyright(c) 2006-2007, Ext JS, LLC.
58887  *
58888  * Originally Released Under LGPL - original licence link has changed is not relivant.
58889  *
58890  * Fork - LGPL
58891  * <script type="text/javascript">
58892  */
58893 /**
58894  * @class Roo.grid.CellSelectionModel
58895  * @extends Roo.grid.AbstractSelectionModel
58896  * This class provides the basic implementation for cell selection in a grid.
58897  * @constructor
58898  * @param {Object} config The object containing the configuration of this model.
58899  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58900  */
58901 Roo.grid.CellSelectionModel = function(config){
58902     Roo.apply(this, config);
58903
58904     this.selection = null;
58905
58906     this.addEvents({
58907         /**
58908              * @event beforerowselect
58909              * Fires before a cell is selected.
58910              * @param {SelectionModel} this
58911              * @param {Number} rowIndex The selected row index
58912              * @param {Number} colIndex The selected cell index
58913              */
58914             "beforecellselect" : true,
58915         /**
58916              * @event cellselect
58917              * Fires when a cell is selected.
58918              * @param {SelectionModel} this
58919              * @param {Number} rowIndex The selected row index
58920              * @param {Number} colIndex The selected cell index
58921              */
58922             "cellselect" : true,
58923         /**
58924              * @event selectionchange
58925              * Fires when the active selection changes.
58926              * @param {SelectionModel} this
58927              * @param {Object} selection null for no selection or an object (o) with two properties
58928                 <ul>
58929                 <li>o.record: the record object for the row the selection is in</li>
58930                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58931                 </ul>
58932              */
58933             "selectionchange" : true,
58934         /**
58935              * @event tabend
58936              * Fires when the tab (or enter) was pressed on the last editable cell
58937              * You can use this to trigger add new row.
58938              * @param {SelectionModel} this
58939              */
58940             "tabend" : true,
58941          /**
58942              * @event beforeeditnext
58943              * Fires before the next editable sell is made active
58944              * You can use this to skip to another cell or fire the tabend
58945              *    if you set cell to false
58946              * @param {Object} eventdata object : { cell : [ row, col ] } 
58947              */
58948             "beforeeditnext" : true
58949     });
58950     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58951 };
58952
58953 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58954     
58955     enter_is_tab: false,
58956
58957     /** @ignore */
58958     initEvents : function(){
58959         this.grid.on("mousedown", this.handleMouseDown, this);
58960         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58961         var view = this.grid.view;
58962         view.on("refresh", this.onViewChange, this);
58963         view.on("rowupdated", this.onRowUpdated, this);
58964         view.on("beforerowremoved", this.clearSelections, this);
58965         view.on("beforerowsinserted", this.clearSelections, this);
58966         if(this.grid.isEditor){
58967             this.grid.on("beforeedit", this.beforeEdit,  this);
58968         }
58969     },
58970
58971         //private
58972     beforeEdit : function(e){
58973         this.select(e.row, e.column, false, true, e.record);
58974     },
58975
58976         //private
58977     onRowUpdated : function(v, index, r){
58978         if(this.selection && this.selection.record == r){
58979             v.onCellSelect(index, this.selection.cell[1]);
58980         }
58981     },
58982
58983         //private
58984     onViewChange : function(){
58985         this.clearSelections(true);
58986     },
58987
58988         /**
58989          * Returns the currently selected cell,.
58990          * @return {Array} The selected cell (row, column) or null if none selected.
58991          */
58992     getSelectedCell : function(){
58993         return this.selection ? this.selection.cell : null;
58994     },
58995
58996     /**
58997      * Clears all selections.
58998      * @param {Boolean} true to prevent the gridview from being notified about the change.
58999      */
59000     clearSelections : function(preventNotify){
59001         var s = this.selection;
59002         if(s){
59003             if(preventNotify !== true){
59004                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59005             }
59006             this.selection = null;
59007             this.fireEvent("selectionchange", this, null);
59008         }
59009     },
59010
59011     /**
59012      * Returns true if there is a selection.
59013      * @return {Boolean}
59014      */
59015     hasSelection : function(){
59016         return this.selection ? true : false;
59017     },
59018
59019     /** @ignore */
59020     handleMouseDown : function(e, t){
59021         var v = this.grid.getView();
59022         if(this.isLocked()){
59023             return;
59024         };
59025         var row = v.findRowIndex(t);
59026         var cell = v.findCellIndex(t);
59027         if(row !== false && cell !== false){
59028             this.select(row, cell);
59029         }
59030     },
59031
59032     /**
59033      * Selects a cell.
59034      * @param {Number} rowIndex
59035      * @param {Number} collIndex
59036      */
59037     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59038         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59039             this.clearSelections();
59040             r = r || this.grid.dataSource.getAt(rowIndex);
59041             this.selection = {
59042                 record : r,
59043                 cell : [rowIndex, colIndex]
59044             };
59045             if(!preventViewNotify){
59046                 var v = this.grid.getView();
59047                 v.onCellSelect(rowIndex, colIndex);
59048                 if(preventFocus !== true){
59049                     v.focusCell(rowIndex, colIndex);
59050                 }
59051             }
59052             this.fireEvent("cellselect", this, rowIndex, colIndex);
59053             this.fireEvent("selectionchange", this, this.selection);
59054         }
59055     },
59056
59057         //private
59058     isSelectable : function(rowIndex, colIndex, cm){
59059         return !cm.isHidden(colIndex);
59060     },
59061
59062     /** @ignore */
59063     handleKeyDown : function(e){
59064         //Roo.log('Cell Sel Model handleKeyDown');
59065         if(!e.isNavKeyPress()){
59066             return;
59067         }
59068         var g = this.grid, s = this.selection;
59069         if(!s){
59070             e.stopEvent();
59071             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59072             if(cell){
59073                 this.select(cell[0], cell[1]);
59074             }
59075             return;
59076         }
59077         var sm = this;
59078         var walk = function(row, col, step){
59079             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59080         };
59081         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59082         var newCell;
59083
59084       
59085
59086         switch(k){
59087             case e.TAB:
59088                 // handled by onEditorKey
59089                 if (g.isEditor && g.editing) {
59090                     return;
59091                 }
59092                 if(e.shiftKey) {
59093                     newCell = walk(r, c-1, -1);
59094                 } else {
59095                     newCell = walk(r, c+1, 1);
59096                 }
59097                 break;
59098             
59099             case e.DOWN:
59100                newCell = walk(r+1, c, 1);
59101                 break;
59102             
59103             case e.UP:
59104                 newCell = walk(r-1, c, -1);
59105                 break;
59106             
59107             case e.RIGHT:
59108                 newCell = walk(r, c+1, 1);
59109                 break;
59110             
59111             case e.LEFT:
59112                 newCell = walk(r, c-1, -1);
59113                 break;
59114             
59115             case e.ENTER:
59116                 
59117                 if(g.isEditor && !g.editing){
59118                    g.startEditing(r, c);
59119                    e.stopEvent();
59120                    return;
59121                 }
59122                 
59123                 
59124              break;
59125         };
59126         if(newCell){
59127             this.select(newCell[0], newCell[1]);
59128             e.stopEvent();
59129             
59130         }
59131     },
59132
59133     acceptsNav : function(row, col, cm){
59134         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59135     },
59136     /**
59137      * Selects a cell.
59138      * @param {Number} field (not used) - as it's normally used as a listener
59139      * @param {Number} e - event - fake it by using
59140      *
59141      * var e = Roo.EventObjectImpl.prototype;
59142      * e.keyCode = e.TAB
59143      *
59144      * 
59145      */
59146     onEditorKey : function(field, e){
59147         
59148         var k = e.getKey(),
59149             newCell,
59150             g = this.grid,
59151             ed = g.activeEditor,
59152             forward = false;
59153         ///Roo.log('onEditorKey' + k);
59154         
59155         
59156         if (this.enter_is_tab && k == e.ENTER) {
59157             k = e.TAB;
59158         }
59159         
59160         if(k == e.TAB){
59161             if(e.shiftKey){
59162                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59163             }else{
59164                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59165                 forward = true;
59166             }
59167             
59168             e.stopEvent();
59169             
59170         } else if(k == e.ENTER &&  !e.ctrlKey){
59171             ed.completeEdit();
59172             e.stopEvent();
59173             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59174         
59175                 } else if(k == e.ESC){
59176             ed.cancelEdit();
59177         }
59178                 
59179         if (newCell) {
59180             var ecall = { cell : newCell, forward : forward };
59181             this.fireEvent('beforeeditnext', ecall );
59182             newCell = ecall.cell;
59183                         forward = ecall.forward;
59184         }
59185                 
59186         if(newCell){
59187             //Roo.log('next cell after edit');
59188             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59189         } else if (forward) {
59190             // tabbed past last
59191             this.fireEvent.defer(100, this, ['tabend',this]);
59192         }
59193     }
59194 });/*
59195  * Based on:
59196  * Ext JS Library 1.1.1
59197  * Copyright(c) 2006-2007, Ext JS, LLC.
59198  *
59199  * Originally Released Under LGPL - original licence link has changed is not relivant.
59200  *
59201  * Fork - LGPL
59202  * <script type="text/javascript">
59203  */
59204  
59205 /**
59206  * @class Roo.grid.EditorGrid
59207  * @extends Roo.grid.Grid
59208  * Class for creating and editable grid.
59209  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59210  * The container MUST have some type of size defined for the grid to fill. The container will be 
59211  * automatically set to position relative if it isn't already.
59212  * @param {Object} dataSource The data model to bind to
59213  * @param {Object} colModel The column model with info about this grid's columns
59214  */
59215 Roo.grid.EditorGrid = function(container, config){
59216     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59217     this.getGridEl().addClass("xedit-grid");
59218
59219     if(!this.selModel){
59220         this.selModel = new Roo.grid.CellSelectionModel();
59221     }
59222
59223     this.activeEditor = null;
59224
59225         this.addEvents({
59226             /**
59227              * @event beforeedit
59228              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59229              * <ul style="padding:5px;padding-left:16px;">
59230              * <li>grid - This grid</li>
59231              * <li>record - The record being edited</li>
59232              * <li>field - The field name being edited</li>
59233              * <li>value - The value for the field being edited.</li>
59234              * <li>row - The grid row index</li>
59235              * <li>column - The grid column index</li>
59236              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59237              * </ul>
59238              * @param {Object} e An edit event (see above for description)
59239              */
59240             "beforeedit" : true,
59241             /**
59242              * @event afteredit
59243              * Fires after a cell is edited. <br />
59244              * <ul style="padding:5px;padding-left:16px;">
59245              * <li>grid - This grid</li>
59246              * <li>record - The record being edited</li>
59247              * <li>field - The field name being edited</li>
59248              * <li>value - The value being set</li>
59249              * <li>originalValue - The original value for the field, before the edit.</li>
59250              * <li>row - The grid row index</li>
59251              * <li>column - The grid column index</li>
59252              * </ul>
59253              * @param {Object} e An edit event (see above for description)
59254              */
59255             "afteredit" : true,
59256             /**
59257              * @event validateedit
59258              * Fires after a cell is edited, but before the value is set in the record. 
59259          * You can use this to modify the value being set in the field, Return false
59260              * to cancel the change. The edit event object has the following properties <br />
59261              * <ul style="padding:5px;padding-left:16px;">
59262          * <li>editor - This editor</li>
59263              * <li>grid - This grid</li>
59264              * <li>record - The record being edited</li>
59265              * <li>field - The field name being edited</li>
59266              * <li>value - The value being set</li>
59267              * <li>originalValue - The original value for the field, before the edit.</li>
59268              * <li>row - The grid row index</li>
59269              * <li>column - The grid column index</li>
59270              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59271              * </ul>
59272              * @param {Object} e An edit event (see above for description)
59273              */
59274             "validateedit" : true
59275         });
59276     this.on("bodyscroll", this.stopEditing,  this);
59277     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59278 };
59279
59280 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59281     /**
59282      * @cfg {Number} clicksToEdit
59283      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59284      */
59285     clicksToEdit: 2,
59286
59287     // private
59288     isEditor : true,
59289     // private
59290     trackMouseOver: false, // causes very odd FF errors
59291
59292     onCellDblClick : function(g, row, col){
59293         this.startEditing(row, col);
59294     },
59295
59296     onEditComplete : function(ed, value, startValue){
59297         this.editing = false;
59298         this.activeEditor = null;
59299         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59300         var r = ed.record;
59301         var field = this.colModel.getDataIndex(ed.col);
59302         var e = {
59303             grid: this,
59304             record: r,
59305             field: field,
59306             originalValue: startValue,
59307             value: value,
59308             row: ed.row,
59309             column: ed.col,
59310             cancel:false,
59311             editor: ed
59312         };
59313         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59314         cell.show();
59315           
59316         if(String(value) !== String(startValue)){
59317             
59318             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59319                 r.set(field, e.value);
59320                 // if we are dealing with a combo box..
59321                 // then we also set the 'name' colum to be the displayField
59322                 if (ed.field.displayField && ed.field.name) {
59323                     r.set(ed.field.name, ed.field.el.dom.value);
59324                 }
59325                 
59326                 delete e.cancel; //?? why!!!
59327                 this.fireEvent("afteredit", e);
59328             }
59329         } else {
59330             this.fireEvent("afteredit", e); // always fire it!
59331         }
59332         this.view.focusCell(ed.row, ed.col);
59333     },
59334
59335     /**
59336      * Starts editing the specified for the specified row/column
59337      * @param {Number} rowIndex
59338      * @param {Number} colIndex
59339      */
59340     startEditing : function(row, col){
59341         this.stopEditing();
59342         if(this.colModel.isCellEditable(col, row)){
59343             this.view.ensureVisible(row, col, true);
59344           
59345             var r = this.dataSource.getAt(row);
59346             var field = this.colModel.getDataIndex(col);
59347             var cell = Roo.get(this.view.getCell(row,col));
59348             var e = {
59349                 grid: this,
59350                 record: r,
59351                 field: field,
59352                 value: r.data[field],
59353                 row: row,
59354                 column: col,
59355                 cancel:false 
59356             };
59357             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59358                 this.editing = true;
59359                 var ed = this.colModel.getCellEditor(col, row);
59360                 
59361                 if (!ed) {
59362                     return;
59363                 }
59364                 if(!ed.rendered){
59365                     ed.render(ed.parentEl || document.body);
59366                 }
59367                 ed.field.reset();
59368                
59369                 cell.hide();
59370                 
59371                 (function(){ // complex but required for focus issues in safari, ie and opera
59372                     ed.row = row;
59373                     ed.col = col;
59374                     ed.record = r;
59375                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59376                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59377                     this.activeEditor = ed;
59378                     var v = r.data[field];
59379                     ed.startEdit(this.view.getCell(row, col), v);
59380                     // combo's with 'displayField and name set
59381                     if (ed.field.displayField && ed.field.name) {
59382                         ed.field.el.dom.value = r.data[ed.field.name];
59383                     }
59384                     
59385                     
59386                 }).defer(50, this);
59387             }
59388         }
59389     },
59390         
59391     /**
59392      * Stops any active editing
59393      */
59394     stopEditing : function(){
59395         if(this.activeEditor){
59396             this.activeEditor.completeEdit();
59397         }
59398         this.activeEditor = null;
59399     },
59400         
59401          /**
59402      * Called to get grid's drag proxy text, by default returns this.ddText.
59403      * @return {String}
59404      */
59405     getDragDropText : function(){
59406         var count = this.selModel.getSelectedCell() ? 1 : 0;
59407         return String.format(this.ddText, count, count == 1 ? '' : 's');
59408     }
59409         
59410 });/*
59411  * Based on:
59412  * Ext JS Library 1.1.1
59413  * Copyright(c) 2006-2007, Ext JS, LLC.
59414  *
59415  * Originally Released Under LGPL - original licence link has changed is not relivant.
59416  *
59417  * Fork - LGPL
59418  * <script type="text/javascript">
59419  */
59420
59421 // private - not really -- you end up using it !
59422 // This is a support class used internally by the Grid components
59423
59424 /**
59425  * @class Roo.grid.GridEditor
59426  * @extends Roo.Editor
59427  * Class for creating and editable grid elements.
59428  * @param {Object} config any settings (must include field)
59429  */
59430 Roo.grid.GridEditor = function(field, config){
59431     if (!config && field.field) {
59432         config = field;
59433         field = Roo.factory(config.field, Roo.form);
59434     }
59435     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59436     field.monitorTab = false;
59437 };
59438
59439 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59440     
59441     /**
59442      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59443      */
59444     
59445     alignment: "tl-tl",
59446     autoSize: "width",
59447     hideEl : false,
59448     cls: "x-small-editor x-grid-editor",
59449     shim:false,
59450     shadow:"frame"
59451 });/*
59452  * Based on:
59453  * Ext JS Library 1.1.1
59454  * Copyright(c) 2006-2007, Ext JS, LLC.
59455  *
59456  * Originally Released Under LGPL - original licence link has changed is not relivant.
59457  *
59458  * Fork - LGPL
59459  * <script type="text/javascript">
59460  */
59461   
59462
59463   
59464 Roo.grid.PropertyRecord = Roo.data.Record.create([
59465     {name:'name',type:'string'},  'value'
59466 ]);
59467
59468
59469 Roo.grid.PropertyStore = function(grid, source){
59470     this.grid = grid;
59471     this.store = new Roo.data.Store({
59472         recordType : Roo.grid.PropertyRecord
59473     });
59474     this.store.on('update', this.onUpdate,  this);
59475     if(source){
59476         this.setSource(source);
59477     }
59478     Roo.grid.PropertyStore.superclass.constructor.call(this);
59479 };
59480
59481
59482
59483 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59484     setSource : function(o){
59485         this.source = o;
59486         this.store.removeAll();
59487         var data = [];
59488         for(var k in o){
59489             if(this.isEditableValue(o[k])){
59490                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59491             }
59492         }
59493         this.store.loadRecords({records: data}, {}, true);
59494     },
59495
59496     onUpdate : function(ds, record, type){
59497         if(type == Roo.data.Record.EDIT){
59498             var v = record.data['value'];
59499             var oldValue = record.modified['value'];
59500             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59501                 this.source[record.id] = v;
59502                 record.commit();
59503                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59504             }else{
59505                 record.reject();
59506             }
59507         }
59508     },
59509
59510     getProperty : function(row){
59511        return this.store.getAt(row);
59512     },
59513
59514     isEditableValue: function(val){
59515         if(val && val instanceof Date){
59516             return true;
59517         }else if(typeof val == 'object' || typeof val == 'function'){
59518             return false;
59519         }
59520         return true;
59521     },
59522
59523     setValue : function(prop, value){
59524         this.source[prop] = value;
59525         this.store.getById(prop).set('value', value);
59526     },
59527
59528     getSource : function(){
59529         return this.source;
59530     }
59531 });
59532
59533 Roo.grid.PropertyColumnModel = function(grid, store){
59534     this.grid = grid;
59535     var g = Roo.grid;
59536     g.PropertyColumnModel.superclass.constructor.call(this, [
59537         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59538         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59539     ]);
59540     this.store = store;
59541     this.bselect = Roo.DomHelper.append(document.body, {
59542         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59543             {tag: 'option', value: 'true', html: 'true'},
59544             {tag: 'option', value: 'false', html: 'false'}
59545         ]
59546     });
59547     Roo.id(this.bselect);
59548     var f = Roo.form;
59549     this.editors = {
59550         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59551         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59552         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59553         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59554         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59555     };
59556     this.renderCellDelegate = this.renderCell.createDelegate(this);
59557     this.renderPropDelegate = this.renderProp.createDelegate(this);
59558 };
59559
59560 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59561     
59562     
59563     nameText : 'Name',
59564     valueText : 'Value',
59565     
59566     dateFormat : 'm/j/Y',
59567     
59568     
59569     renderDate : function(dateVal){
59570         return dateVal.dateFormat(this.dateFormat);
59571     },
59572
59573     renderBool : function(bVal){
59574         return bVal ? 'true' : 'false';
59575     },
59576
59577     isCellEditable : function(colIndex, rowIndex){
59578         return colIndex == 1;
59579     },
59580
59581     getRenderer : function(col){
59582         return col == 1 ?
59583             this.renderCellDelegate : this.renderPropDelegate;
59584     },
59585
59586     renderProp : function(v){
59587         return this.getPropertyName(v);
59588     },
59589
59590     renderCell : function(val){
59591         var rv = val;
59592         if(val instanceof Date){
59593             rv = this.renderDate(val);
59594         }else if(typeof val == 'boolean'){
59595             rv = this.renderBool(val);
59596         }
59597         return Roo.util.Format.htmlEncode(rv);
59598     },
59599
59600     getPropertyName : function(name){
59601         var pn = this.grid.propertyNames;
59602         return pn && pn[name] ? pn[name] : name;
59603     },
59604
59605     getCellEditor : function(colIndex, rowIndex){
59606         var p = this.store.getProperty(rowIndex);
59607         var n = p.data['name'], val = p.data['value'];
59608         
59609         if(typeof(this.grid.customEditors[n]) == 'string'){
59610             return this.editors[this.grid.customEditors[n]];
59611         }
59612         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59613             return this.grid.customEditors[n];
59614         }
59615         if(val instanceof Date){
59616             return this.editors['date'];
59617         }else if(typeof val == 'number'){
59618             return this.editors['number'];
59619         }else if(typeof val == 'boolean'){
59620             return this.editors['boolean'];
59621         }else{
59622             return this.editors['string'];
59623         }
59624     }
59625 });
59626
59627 /**
59628  * @class Roo.grid.PropertyGrid
59629  * @extends Roo.grid.EditorGrid
59630  * This class represents the  interface of a component based property grid control.
59631  * <br><br>Usage:<pre><code>
59632  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59633       
59634  });
59635  // set any options
59636  grid.render();
59637  * </code></pre>
59638   
59639  * @constructor
59640  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59641  * The container MUST have some type of size defined for the grid to fill. The container will be
59642  * automatically set to position relative if it isn't already.
59643  * @param {Object} config A config object that sets properties on this grid.
59644  */
59645 Roo.grid.PropertyGrid = function(container, config){
59646     config = config || {};
59647     var store = new Roo.grid.PropertyStore(this);
59648     this.store = store;
59649     var cm = new Roo.grid.PropertyColumnModel(this, store);
59650     store.store.sort('name', 'ASC');
59651     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59652         ds: store.store,
59653         cm: cm,
59654         enableColLock:false,
59655         enableColumnMove:false,
59656         stripeRows:false,
59657         trackMouseOver: false,
59658         clicksToEdit:1
59659     }, config));
59660     this.getGridEl().addClass('x-props-grid');
59661     this.lastEditRow = null;
59662     this.on('columnresize', this.onColumnResize, this);
59663     this.addEvents({
59664          /**
59665              * @event beforepropertychange
59666              * Fires before a property changes (return false to stop?)
59667              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59668              * @param {String} id Record Id
59669              * @param {String} newval New Value
59670          * @param {String} oldval Old Value
59671              */
59672         "beforepropertychange": true,
59673         /**
59674              * @event propertychange
59675              * Fires after a property changes
59676              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59677              * @param {String} id Record Id
59678              * @param {String} newval New Value
59679          * @param {String} oldval Old Value
59680              */
59681         "propertychange": true
59682     });
59683     this.customEditors = this.customEditors || {};
59684 };
59685 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59686     
59687      /**
59688      * @cfg {Object} customEditors map of colnames=> custom editors.
59689      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59690      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59691      * false disables editing of the field.
59692          */
59693     
59694       /**
59695      * @cfg {Object} propertyNames map of property Names to their displayed value
59696          */
59697     
59698     render : function(){
59699         Roo.grid.PropertyGrid.superclass.render.call(this);
59700         this.autoSize.defer(100, this);
59701     },
59702
59703     autoSize : function(){
59704         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59705         if(this.view){
59706             this.view.fitColumns();
59707         }
59708     },
59709
59710     onColumnResize : function(){
59711         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59712         this.autoSize();
59713     },
59714     /**
59715      * Sets the data for the Grid
59716      * accepts a Key => Value object of all the elements avaiable.
59717      * @param {Object} data  to appear in grid.
59718      */
59719     setSource : function(source){
59720         this.store.setSource(source);
59721         //this.autoSize();
59722     },
59723     /**
59724      * Gets all the data from the grid.
59725      * @return {Object} data  data stored in grid
59726      */
59727     getSource : function(){
59728         return this.store.getSource();
59729     }
59730 });/*
59731   
59732  * Licence LGPL
59733  
59734  */
59735  
59736 /**
59737  * @class Roo.grid.Calendar
59738  * @extends Roo.util.Grid
59739  * This class extends the Grid to provide a calendar widget
59740  * <br><br>Usage:<pre><code>
59741  var grid = new Roo.grid.Calendar("my-container-id", {
59742      ds: myDataStore,
59743      cm: myColModel,
59744      selModel: mySelectionModel,
59745      autoSizeColumns: true,
59746      monitorWindowResize: false,
59747      trackMouseOver: true
59748      eventstore : real data store..
59749  });
59750  // set any options
59751  grid.render();
59752   
59753   * @constructor
59754  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59755  * The container MUST have some type of size defined for the grid to fill. The container will be
59756  * automatically set to position relative if it isn't already.
59757  * @param {Object} config A config object that sets properties on this grid.
59758  */
59759 Roo.grid.Calendar = function(container, config){
59760         // initialize the container
59761         this.container = Roo.get(container);
59762         this.container.update("");
59763         this.container.setStyle("overflow", "hidden");
59764     this.container.addClass('x-grid-container');
59765
59766     this.id = this.container.id;
59767
59768     Roo.apply(this, config);
59769     // check and correct shorthanded configs
59770     
59771     var rows = [];
59772     var d =1;
59773     for (var r = 0;r < 6;r++) {
59774         
59775         rows[r]=[];
59776         for (var c =0;c < 7;c++) {
59777             rows[r][c]= '';
59778         }
59779     }
59780     if (this.eventStore) {
59781         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59782         this.eventStore.on('load',this.onLoad, this);
59783         this.eventStore.on('beforeload',this.clearEvents, this);
59784          
59785     }
59786     
59787     this.dataSource = new Roo.data.Store({
59788             proxy: new Roo.data.MemoryProxy(rows),
59789             reader: new Roo.data.ArrayReader({}, [
59790                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59791     });
59792
59793     this.dataSource.load();
59794     this.ds = this.dataSource;
59795     this.ds.xmodule = this.xmodule || false;
59796     
59797     
59798     var cellRender = function(v,x,r)
59799     {
59800         return String.format(
59801             '<div class="fc-day  fc-widget-content"><div>' +
59802                 '<div class="fc-event-container"></div>' +
59803                 '<div class="fc-day-number">{0}</div>'+
59804                 
59805                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59806             '</div></div>', v);
59807     
59808     }
59809     
59810     
59811     this.colModel = new Roo.grid.ColumnModel( [
59812         {
59813             xtype: 'ColumnModel',
59814             xns: Roo.grid,
59815             dataIndex : 'weekday0',
59816             header : 'Sunday',
59817             renderer : cellRender
59818         },
59819         {
59820             xtype: 'ColumnModel',
59821             xns: Roo.grid,
59822             dataIndex : 'weekday1',
59823             header : 'Monday',
59824             renderer : cellRender
59825         },
59826         {
59827             xtype: 'ColumnModel',
59828             xns: Roo.grid,
59829             dataIndex : 'weekday2',
59830             header : 'Tuesday',
59831             renderer : cellRender
59832         },
59833         {
59834             xtype: 'ColumnModel',
59835             xns: Roo.grid,
59836             dataIndex : 'weekday3',
59837             header : 'Wednesday',
59838             renderer : cellRender
59839         },
59840         {
59841             xtype: 'ColumnModel',
59842             xns: Roo.grid,
59843             dataIndex : 'weekday4',
59844             header : 'Thursday',
59845             renderer : cellRender
59846         },
59847         {
59848             xtype: 'ColumnModel',
59849             xns: Roo.grid,
59850             dataIndex : 'weekday5',
59851             header : 'Friday',
59852             renderer : cellRender
59853         },
59854         {
59855             xtype: 'ColumnModel',
59856             xns: Roo.grid,
59857             dataIndex : 'weekday6',
59858             header : 'Saturday',
59859             renderer : cellRender
59860         }
59861     ]);
59862     this.cm = this.colModel;
59863     this.cm.xmodule = this.xmodule || false;
59864  
59865         
59866           
59867     //this.selModel = new Roo.grid.CellSelectionModel();
59868     //this.sm = this.selModel;
59869     //this.selModel.init(this);
59870     
59871     
59872     if(this.width){
59873         this.container.setWidth(this.width);
59874     }
59875
59876     if(this.height){
59877         this.container.setHeight(this.height);
59878     }
59879     /** @private */
59880         this.addEvents({
59881         // raw events
59882         /**
59883          * @event click
59884          * The raw click event for the entire grid.
59885          * @param {Roo.EventObject} e
59886          */
59887         "click" : true,
59888         /**
59889          * @event dblclick
59890          * The raw dblclick event for the entire grid.
59891          * @param {Roo.EventObject} e
59892          */
59893         "dblclick" : true,
59894         /**
59895          * @event contextmenu
59896          * The raw contextmenu event for the entire grid.
59897          * @param {Roo.EventObject} e
59898          */
59899         "contextmenu" : true,
59900         /**
59901          * @event mousedown
59902          * The raw mousedown event for the entire grid.
59903          * @param {Roo.EventObject} e
59904          */
59905         "mousedown" : true,
59906         /**
59907          * @event mouseup
59908          * The raw mouseup event for the entire grid.
59909          * @param {Roo.EventObject} e
59910          */
59911         "mouseup" : true,
59912         /**
59913          * @event mouseover
59914          * The raw mouseover event for the entire grid.
59915          * @param {Roo.EventObject} e
59916          */
59917         "mouseover" : true,
59918         /**
59919          * @event mouseout
59920          * The raw mouseout event for the entire grid.
59921          * @param {Roo.EventObject} e
59922          */
59923         "mouseout" : true,
59924         /**
59925          * @event keypress
59926          * The raw keypress event for the entire grid.
59927          * @param {Roo.EventObject} e
59928          */
59929         "keypress" : true,
59930         /**
59931          * @event keydown
59932          * The raw keydown event for the entire grid.
59933          * @param {Roo.EventObject} e
59934          */
59935         "keydown" : true,
59936
59937         // custom events
59938
59939         /**
59940          * @event cellclick
59941          * Fires when a cell is clicked
59942          * @param {Grid} this
59943          * @param {Number} rowIndex
59944          * @param {Number} columnIndex
59945          * @param {Roo.EventObject} e
59946          */
59947         "cellclick" : true,
59948         /**
59949          * @event celldblclick
59950          * Fires when a cell is double clicked
59951          * @param {Grid} this
59952          * @param {Number} rowIndex
59953          * @param {Number} columnIndex
59954          * @param {Roo.EventObject} e
59955          */
59956         "celldblclick" : true,
59957         /**
59958          * @event rowclick
59959          * Fires when a row is clicked
59960          * @param {Grid} this
59961          * @param {Number} rowIndex
59962          * @param {Roo.EventObject} e
59963          */
59964         "rowclick" : true,
59965         /**
59966          * @event rowdblclick
59967          * Fires when a row is double clicked
59968          * @param {Grid} this
59969          * @param {Number} rowIndex
59970          * @param {Roo.EventObject} e
59971          */
59972         "rowdblclick" : true,
59973         /**
59974          * @event headerclick
59975          * Fires when a header is clicked
59976          * @param {Grid} this
59977          * @param {Number} columnIndex
59978          * @param {Roo.EventObject} e
59979          */
59980         "headerclick" : true,
59981         /**
59982          * @event headerdblclick
59983          * Fires when a header cell is double clicked
59984          * @param {Grid} this
59985          * @param {Number} columnIndex
59986          * @param {Roo.EventObject} e
59987          */
59988         "headerdblclick" : true,
59989         /**
59990          * @event rowcontextmenu
59991          * Fires when a row is right clicked
59992          * @param {Grid} this
59993          * @param {Number} rowIndex
59994          * @param {Roo.EventObject} e
59995          */
59996         "rowcontextmenu" : true,
59997         /**
59998          * @event cellcontextmenu
59999          * Fires when a cell is right clicked
60000          * @param {Grid} this
60001          * @param {Number} rowIndex
60002          * @param {Number} cellIndex
60003          * @param {Roo.EventObject} e
60004          */
60005          "cellcontextmenu" : true,
60006         /**
60007          * @event headercontextmenu
60008          * Fires when a header is right clicked
60009          * @param {Grid} this
60010          * @param {Number} columnIndex
60011          * @param {Roo.EventObject} e
60012          */
60013         "headercontextmenu" : true,
60014         /**
60015          * @event bodyscroll
60016          * Fires when the body element is scrolled
60017          * @param {Number} scrollLeft
60018          * @param {Number} scrollTop
60019          */
60020         "bodyscroll" : true,
60021         /**
60022          * @event columnresize
60023          * Fires when the user resizes a column
60024          * @param {Number} columnIndex
60025          * @param {Number} newSize
60026          */
60027         "columnresize" : true,
60028         /**
60029          * @event columnmove
60030          * Fires when the user moves a column
60031          * @param {Number} oldIndex
60032          * @param {Number} newIndex
60033          */
60034         "columnmove" : true,
60035         /**
60036          * @event startdrag
60037          * Fires when row(s) start being dragged
60038          * @param {Grid} this
60039          * @param {Roo.GridDD} dd The drag drop object
60040          * @param {event} e The raw browser event
60041          */
60042         "startdrag" : true,
60043         /**
60044          * @event enddrag
60045          * Fires when a drag operation is complete
60046          * @param {Grid} this
60047          * @param {Roo.GridDD} dd The drag drop object
60048          * @param {event} e The raw browser event
60049          */
60050         "enddrag" : true,
60051         /**
60052          * @event dragdrop
60053          * Fires when dragged row(s) are dropped on a valid DD target
60054          * @param {Grid} this
60055          * @param {Roo.GridDD} dd The drag drop object
60056          * @param {String} targetId The target drag drop object
60057          * @param {event} e The raw browser event
60058          */
60059         "dragdrop" : true,
60060         /**
60061          * @event dragover
60062          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60063          * @param {Grid} this
60064          * @param {Roo.GridDD} dd The drag drop object
60065          * @param {String} targetId The target drag drop object
60066          * @param {event} e The raw browser event
60067          */
60068         "dragover" : true,
60069         /**
60070          * @event dragenter
60071          *  Fires when the dragged row(s) first cross another DD target while being dragged
60072          * @param {Grid} this
60073          * @param {Roo.GridDD} dd The drag drop object
60074          * @param {String} targetId The target drag drop object
60075          * @param {event} e The raw browser event
60076          */
60077         "dragenter" : true,
60078         /**
60079          * @event dragout
60080          * Fires when the dragged row(s) leave another DD target while being dragged
60081          * @param {Grid} this
60082          * @param {Roo.GridDD} dd The drag drop object
60083          * @param {String} targetId The target drag drop object
60084          * @param {event} e The raw browser event
60085          */
60086         "dragout" : true,
60087         /**
60088          * @event rowclass
60089          * Fires when a row is rendered, so you can change add a style to it.
60090          * @param {GridView} gridview   The grid view
60091          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60092          */
60093         'rowclass' : true,
60094
60095         /**
60096          * @event render
60097          * Fires when the grid is rendered
60098          * @param {Grid} grid
60099          */
60100         'render' : true,
60101             /**
60102              * @event select
60103              * Fires when a date is selected
60104              * @param {DatePicker} this
60105              * @param {Date} date The selected date
60106              */
60107         'select': true,
60108         /**
60109              * @event monthchange
60110              * Fires when the displayed month changes 
60111              * @param {DatePicker} this
60112              * @param {Date} date The selected month
60113              */
60114         'monthchange': true,
60115         /**
60116              * @event evententer
60117              * Fires when mouse over an event
60118              * @param {Calendar} this
60119              * @param {event} Event
60120              */
60121         'evententer': true,
60122         /**
60123              * @event eventleave
60124              * Fires when the mouse leaves an
60125              * @param {Calendar} this
60126              * @param {event}
60127              */
60128         'eventleave': true,
60129         /**
60130              * @event eventclick
60131              * Fires when the mouse click an
60132              * @param {Calendar} this
60133              * @param {event}
60134              */
60135         'eventclick': true,
60136         /**
60137              * @event eventrender
60138              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60139              * @param {Calendar} this
60140              * @param {data} data to be modified
60141              */
60142         'eventrender': true
60143         
60144     });
60145
60146     Roo.grid.Grid.superclass.constructor.call(this);
60147     this.on('render', function() {
60148         this.view.el.addClass('x-grid-cal'); 
60149         
60150         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60151
60152     },this);
60153     
60154     if (!Roo.grid.Calendar.style) {
60155         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60156             
60157             
60158             '.x-grid-cal .x-grid-col' :  {
60159                 height: 'auto !important',
60160                 'vertical-align': 'top'
60161             },
60162             '.x-grid-cal  .fc-event-hori' : {
60163                 height: '14px'
60164             }
60165              
60166             
60167         }, Roo.id());
60168     }
60169
60170     
60171     
60172 };
60173 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60174     /**
60175      * @cfg {Store} eventStore The store that loads events.
60176      */
60177     eventStore : 25,
60178
60179      
60180     activeDate : false,
60181     startDay : 0,
60182     autoWidth : true,
60183     monitorWindowResize : false,
60184
60185     
60186     resizeColumns : function() {
60187         var col = (this.view.el.getWidth() / 7) - 3;
60188         // loop through cols, and setWidth
60189         for(var i =0 ; i < 7 ; i++){
60190             this.cm.setColumnWidth(i, col);
60191         }
60192     },
60193      setDate :function(date) {
60194         
60195         Roo.log('setDate?');
60196         
60197         this.resizeColumns();
60198         var vd = this.activeDate;
60199         this.activeDate = date;
60200 //        if(vd && this.el){
60201 //            var t = date.getTime();
60202 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60203 //                Roo.log('using add remove');
60204 //                
60205 //                this.fireEvent('monthchange', this, date);
60206 //                
60207 //                this.cells.removeClass("fc-state-highlight");
60208 //                this.cells.each(function(c){
60209 //                   if(c.dateValue == t){
60210 //                       c.addClass("fc-state-highlight");
60211 //                       setTimeout(function(){
60212 //                            try{c.dom.firstChild.focus();}catch(e){}
60213 //                       }, 50);
60214 //                       return false;
60215 //                   }
60216 //                   return true;
60217 //                });
60218 //                return;
60219 //            }
60220 //        }
60221         
60222         var days = date.getDaysInMonth();
60223         
60224         var firstOfMonth = date.getFirstDateOfMonth();
60225         var startingPos = firstOfMonth.getDay()-this.startDay;
60226         
60227         if(startingPos < this.startDay){
60228             startingPos += 7;
60229         }
60230         
60231         var pm = date.add(Date.MONTH, -1);
60232         var prevStart = pm.getDaysInMonth()-startingPos;
60233 //        
60234         
60235         
60236         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60237         
60238         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60239         //this.cells.addClassOnOver('fc-state-hover');
60240         
60241         var cells = this.cells.elements;
60242         var textEls = this.textNodes;
60243         
60244         //Roo.each(cells, function(cell){
60245         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60246         //});
60247         
60248         days += startingPos;
60249
60250         // convert everything to numbers so it's fast
60251         var day = 86400000;
60252         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60253         //Roo.log(d);
60254         //Roo.log(pm);
60255         //Roo.log(prevStart);
60256         
60257         var today = new Date().clearTime().getTime();
60258         var sel = date.clearTime().getTime();
60259         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60260         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60261         var ddMatch = this.disabledDatesRE;
60262         var ddText = this.disabledDatesText;
60263         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60264         var ddaysText = this.disabledDaysText;
60265         var format = this.format;
60266         
60267         var setCellClass = function(cal, cell){
60268             
60269             //Roo.log('set Cell Class');
60270             cell.title = "";
60271             var t = d.getTime();
60272             
60273             //Roo.log(d);
60274             
60275             
60276             cell.dateValue = t;
60277             if(t == today){
60278                 cell.className += " fc-today";
60279                 cell.className += " fc-state-highlight";
60280                 cell.title = cal.todayText;
60281             }
60282             if(t == sel){
60283                 // disable highlight in other month..
60284                 cell.className += " fc-state-highlight";
60285                 
60286             }
60287             // disabling
60288             if(t < min) {
60289                 //cell.className = " fc-state-disabled";
60290                 cell.title = cal.minText;
60291                 return;
60292             }
60293             if(t > max) {
60294                 //cell.className = " fc-state-disabled";
60295                 cell.title = cal.maxText;
60296                 return;
60297             }
60298             if(ddays){
60299                 if(ddays.indexOf(d.getDay()) != -1){
60300                     // cell.title = ddaysText;
60301                    // cell.className = " fc-state-disabled";
60302                 }
60303             }
60304             if(ddMatch && format){
60305                 var fvalue = d.dateFormat(format);
60306                 if(ddMatch.test(fvalue)){
60307                     cell.title = ddText.replace("%0", fvalue);
60308                    cell.className = " fc-state-disabled";
60309                 }
60310             }
60311             
60312             if (!cell.initialClassName) {
60313                 cell.initialClassName = cell.dom.className;
60314             }
60315             
60316             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60317         };
60318
60319         var i = 0;
60320         
60321         for(; i < startingPos; i++) {
60322             cells[i].dayName =  (++prevStart);
60323             Roo.log(textEls[i]);
60324             d.setDate(d.getDate()+1);
60325             
60326             //cells[i].className = "fc-past fc-other-month";
60327             setCellClass(this, cells[i]);
60328         }
60329         
60330         var intDay = 0;
60331         
60332         for(; i < days; i++){
60333             intDay = i - startingPos + 1;
60334             cells[i].dayName =  (intDay);
60335             d.setDate(d.getDate()+1);
60336             
60337             cells[i].className = ''; // "x-date-active";
60338             setCellClass(this, cells[i]);
60339         }
60340         var extraDays = 0;
60341         
60342         for(; i < 42; i++) {
60343             //textEls[i].innerHTML = (++extraDays);
60344             
60345             d.setDate(d.getDate()+1);
60346             cells[i].dayName = (++extraDays);
60347             cells[i].className = "fc-future fc-other-month";
60348             setCellClass(this, cells[i]);
60349         }
60350         
60351         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60352         
60353         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60354         
60355         // this will cause all the cells to mis
60356         var rows= [];
60357         var i =0;
60358         for (var r = 0;r < 6;r++) {
60359             for (var c =0;c < 7;c++) {
60360                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60361             }    
60362         }
60363         
60364         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60365         for(i=0;i<cells.length;i++) {
60366             
60367             this.cells.elements[i].dayName = cells[i].dayName ;
60368             this.cells.elements[i].className = cells[i].className;
60369             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60370             this.cells.elements[i].title = cells[i].title ;
60371             this.cells.elements[i].dateValue = cells[i].dateValue ;
60372         }
60373         
60374         
60375         
60376         
60377         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60378         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60379         
60380         ////if(totalRows != 6){
60381             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60382            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60383        // }
60384         
60385         this.fireEvent('monthchange', this, date);
60386         
60387         
60388     },
60389  /**
60390      * Returns the grid's SelectionModel.
60391      * @return {SelectionModel}
60392      */
60393     getSelectionModel : function(){
60394         if(!this.selModel){
60395             this.selModel = new Roo.grid.CellSelectionModel();
60396         }
60397         return this.selModel;
60398     },
60399
60400     load: function() {
60401         this.eventStore.load()
60402         
60403         
60404         
60405     },
60406     
60407     findCell : function(dt) {
60408         dt = dt.clearTime().getTime();
60409         var ret = false;
60410         this.cells.each(function(c){
60411             //Roo.log("check " +c.dateValue + '?=' + dt);
60412             if(c.dateValue == dt){
60413                 ret = c;
60414                 return false;
60415             }
60416             return true;
60417         });
60418         
60419         return ret;
60420     },
60421     
60422     findCells : function(rec) {
60423         var s = rec.data.start_dt.clone().clearTime().getTime();
60424        // Roo.log(s);
60425         var e= rec.data.end_dt.clone().clearTime().getTime();
60426        // Roo.log(e);
60427         var ret = [];
60428         this.cells.each(function(c){
60429              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60430             
60431             if(c.dateValue > e){
60432                 return ;
60433             }
60434             if(c.dateValue < s){
60435                 return ;
60436             }
60437             ret.push(c);
60438         });
60439         
60440         return ret;    
60441     },
60442     
60443     findBestRow: function(cells)
60444     {
60445         var ret = 0;
60446         
60447         for (var i =0 ; i < cells.length;i++) {
60448             ret  = Math.max(cells[i].rows || 0,ret);
60449         }
60450         return ret;
60451         
60452     },
60453     
60454     
60455     addItem : function(rec)
60456     {
60457         // look for vertical location slot in
60458         var cells = this.findCells(rec);
60459         
60460         rec.row = this.findBestRow(cells);
60461         
60462         // work out the location.
60463         
60464         var crow = false;
60465         var rows = [];
60466         for(var i =0; i < cells.length; i++) {
60467             if (!crow) {
60468                 crow = {
60469                     start : cells[i],
60470                     end :  cells[i]
60471                 };
60472                 continue;
60473             }
60474             if (crow.start.getY() == cells[i].getY()) {
60475                 // on same row.
60476                 crow.end = cells[i];
60477                 continue;
60478             }
60479             // different row.
60480             rows.push(crow);
60481             crow = {
60482                 start: cells[i],
60483                 end : cells[i]
60484             };
60485             
60486         }
60487         
60488         rows.push(crow);
60489         rec.els = [];
60490         rec.rows = rows;
60491         rec.cells = cells;
60492         for (var i = 0; i < cells.length;i++) {
60493             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60494             
60495         }
60496         
60497         
60498     },
60499     
60500     clearEvents: function() {
60501         
60502         if (!this.eventStore.getCount()) {
60503             return;
60504         }
60505         // reset number of rows in cells.
60506         Roo.each(this.cells.elements, function(c){
60507             c.rows = 0;
60508         });
60509         
60510         this.eventStore.each(function(e) {
60511             this.clearEvent(e);
60512         },this);
60513         
60514     },
60515     
60516     clearEvent : function(ev)
60517     {
60518         if (ev.els) {
60519             Roo.each(ev.els, function(el) {
60520                 el.un('mouseenter' ,this.onEventEnter, this);
60521                 el.un('mouseleave' ,this.onEventLeave, this);
60522                 el.remove();
60523             },this);
60524             ev.els = [];
60525         }
60526     },
60527     
60528     
60529     renderEvent : function(ev,ctr) {
60530         if (!ctr) {
60531              ctr = this.view.el.select('.fc-event-container',true).first();
60532         }
60533         
60534          
60535         this.clearEvent(ev);
60536             //code
60537        
60538         
60539         
60540         ev.els = [];
60541         var cells = ev.cells;
60542         var rows = ev.rows;
60543         this.fireEvent('eventrender', this, ev);
60544         
60545         for(var i =0; i < rows.length; i++) {
60546             
60547             cls = '';
60548             if (i == 0) {
60549                 cls += ' fc-event-start';
60550             }
60551             if ((i+1) == rows.length) {
60552                 cls += ' fc-event-end';
60553             }
60554             
60555             //Roo.log(ev.data);
60556             // how many rows should it span..
60557             var cg = this.eventTmpl.append(ctr,Roo.apply({
60558                 fccls : cls
60559                 
60560             }, ev.data) , true);
60561             
60562             
60563             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60564             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60565             cg.on('click', this.onEventClick, this, ev);
60566             
60567             ev.els.push(cg);
60568             
60569             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60570             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60571             //Roo.log(cg);
60572              
60573             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60574             cg.setWidth(ebox.right - sbox.x -2);
60575         }
60576     },
60577     
60578     renderEvents: function()
60579     {   
60580         // first make sure there is enough space..
60581         
60582         if (!this.eventTmpl) {
60583             this.eventTmpl = new Roo.Template(
60584                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60585                     '<div class="fc-event-inner">' +
60586                         '<span class="fc-event-time">{time}</span>' +
60587                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60588                     '</div>' +
60589                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60590                 '</div>'
60591             );
60592                 
60593         }
60594                
60595         
60596         
60597         this.cells.each(function(c) {
60598             //Roo.log(c.select('.fc-day-content div',true).first());
60599             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60600         });
60601         
60602         var ctr = this.view.el.select('.fc-event-container',true).first();
60603         
60604         var cls;
60605         this.eventStore.each(function(ev){
60606             
60607             this.renderEvent(ev);
60608              
60609              
60610         }, this);
60611         this.view.layout();
60612         
60613     },
60614     
60615     onEventEnter: function (e, el,event,d) {
60616         this.fireEvent('evententer', this, el, event);
60617     },
60618     
60619     onEventLeave: function (e, el,event,d) {
60620         this.fireEvent('eventleave', this, el, event);
60621     },
60622     
60623     onEventClick: function (e, el,event,d) {
60624         this.fireEvent('eventclick', this, el, event);
60625     },
60626     
60627     onMonthChange: function () {
60628         this.store.load();
60629     },
60630     
60631     onLoad: function () {
60632         
60633         //Roo.log('calendar onload');
60634 //         
60635         if(this.eventStore.getCount() > 0){
60636             
60637            
60638             
60639             this.eventStore.each(function(d){
60640                 
60641                 
60642                 // FIXME..
60643                 var add =   d.data;
60644                 if (typeof(add.end_dt) == 'undefined')  {
60645                     Roo.log("Missing End time in calendar data: ");
60646                     Roo.log(d);
60647                     return;
60648                 }
60649                 if (typeof(add.start_dt) == 'undefined')  {
60650                     Roo.log("Missing Start time in calendar data: ");
60651                     Roo.log(d);
60652                     return;
60653                 }
60654                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60655                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60656                 add.id = add.id || d.id;
60657                 add.title = add.title || '??';
60658                 
60659                 this.addItem(d);
60660                 
60661              
60662             },this);
60663         }
60664         
60665         this.renderEvents();
60666     }
60667     
60668
60669 });
60670 /*
60671  grid : {
60672                 xtype: 'Grid',
60673                 xns: Roo.grid,
60674                 listeners : {
60675                     render : function ()
60676                     {
60677                         _this.grid = this;
60678                         
60679                         if (!this.view.el.hasClass('course-timesheet')) {
60680                             this.view.el.addClass('course-timesheet');
60681                         }
60682                         if (this.tsStyle) {
60683                             this.ds.load({});
60684                             return; 
60685                         }
60686                         Roo.log('width');
60687                         Roo.log(_this.grid.view.el.getWidth());
60688                         
60689                         
60690                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60691                             '.course-timesheet .x-grid-row' : {
60692                                 height: '80px'
60693                             },
60694                             '.x-grid-row td' : {
60695                                 'vertical-align' : 0
60696                             },
60697                             '.course-edit-link' : {
60698                                 'color' : 'blue',
60699                                 'text-overflow' : 'ellipsis',
60700                                 'overflow' : 'hidden',
60701                                 'white-space' : 'nowrap',
60702                                 'cursor' : 'pointer'
60703                             },
60704                             '.sub-link' : {
60705                                 'color' : 'green'
60706                             },
60707                             '.de-act-sup-link' : {
60708                                 'color' : 'purple',
60709                                 'text-decoration' : 'line-through'
60710                             },
60711                             '.de-act-link' : {
60712                                 'color' : 'red',
60713                                 'text-decoration' : 'line-through'
60714                             },
60715                             '.course-timesheet .course-highlight' : {
60716                                 'border-top-style': 'dashed !important',
60717                                 'border-bottom-bottom': 'dashed !important'
60718                             },
60719                             '.course-timesheet .course-item' : {
60720                                 'font-family'   : 'tahoma, arial, helvetica',
60721                                 'font-size'     : '11px',
60722                                 'overflow'      : 'hidden',
60723                                 'padding-left'  : '10px',
60724                                 'padding-right' : '10px',
60725                                 'padding-top' : '10px' 
60726                             }
60727                             
60728                         }, Roo.id());
60729                                 this.ds.load({});
60730                     }
60731                 },
60732                 autoWidth : true,
60733                 monitorWindowResize : false,
60734                 cellrenderer : function(v,x,r)
60735                 {
60736                     return v;
60737                 },
60738                 sm : {
60739                     xtype: 'CellSelectionModel',
60740                     xns: Roo.grid
60741                 },
60742                 dataSource : {
60743                     xtype: 'Store',
60744                     xns: Roo.data,
60745                     listeners : {
60746                         beforeload : function (_self, options)
60747                         {
60748                             options.params = options.params || {};
60749                             options.params._month = _this.monthField.getValue();
60750                             options.params.limit = 9999;
60751                             options.params['sort'] = 'when_dt';    
60752                             options.params['dir'] = 'ASC';    
60753                             this.proxy.loadResponse = this.loadResponse;
60754                             Roo.log("load?");
60755                             //this.addColumns();
60756                         },
60757                         load : function (_self, records, options)
60758                         {
60759                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60760                                 // if you click on the translation.. you can edit it...
60761                                 var el = Roo.get(this);
60762                                 var id = el.dom.getAttribute('data-id');
60763                                 var d = el.dom.getAttribute('data-date');
60764                                 var t = el.dom.getAttribute('data-time');
60765                                 //var id = this.child('span').dom.textContent;
60766                                 
60767                                 //Roo.log(this);
60768                                 Pman.Dialog.CourseCalendar.show({
60769                                     id : id,
60770                                     when_d : d,
60771                                     when_t : t,
60772                                     productitem_active : id ? 1 : 0
60773                                 }, function() {
60774                                     _this.grid.ds.load({});
60775                                 });
60776                            
60777                            });
60778                            
60779                            _this.panel.fireEvent('resize', [ '', '' ]);
60780                         }
60781                     },
60782                     loadResponse : function(o, success, response){
60783                             // this is overridden on before load..
60784                             
60785                             Roo.log("our code?");       
60786                             //Roo.log(success);
60787                             //Roo.log(response)
60788                             delete this.activeRequest;
60789                             if(!success){
60790                                 this.fireEvent("loadexception", this, o, response);
60791                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60792                                 return;
60793                             }
60794                             var result;
60795                             try {
60796                                 result = o.reader.read(response);
60797                             }catch(e){
60798                                 Roo.log("load exception?");
60799                                 this.fireEvent("loadexception", this, o, response, e);
60800                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60801                                 return;
60802                             }
60803                             Roo.log("ready...");        
60804                             // loop through result.records;
60805                             // and set this.tdate[date] = [] << array of records..
60806                             _this.tdata  = {};
60807                             Roo.each(result.records, function(r){
60808                                 //Roo.log(r.data);
60809                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60810                                     _this.tdata[r.data.when_dt.format('j')] = [];
60811                                 }
60812                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60813                             });
60814                             
60815                             //Roo.log(_this.tdata);
60816                             
60817                             result.records = [];
60818                             result.totalRecords = 6;
60819                     
60820                             // let's generate some duumy records for the rows.
60821                             //var st = _this.dateField.getValue();
60822                             
60823                             // work out monday..
60824                             //st = st.add(Date.DAY, -1 * st.format('w'));
60825                             
60826                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60827                             
60828                             var firstOfMonth = date.getFirstDayOfMonth();
60829                             var days = date.getDaysInMonth();
60830                             var d = 1;
60831                             var firstAdded = false;
60832                             for (var i = 0; i < result.totalRecords ; i++) {
60833                                 //var d= st.add(Date.DAY, i);
60834                                 var row = {};
60835                                 var added = 0;
60836                                 for(var w = 0 ; w < 7 ; w++){
60837                                     if(!firstAdded && firstOfMonth != w){
60838                                         continue;
60839                                     }
60840                                     if(d > days){
60841                                         continue;
60842                                     }
60843                                     firstAdded = true;
60844                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60845                                     row['weekday'+w] = String.format(
60846                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60847                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60848                                                     d,
60849                                                     date.format('Y-m-')+dd
60850                                                 );
60851                                     added++;
60852                                     if(typeof(_this.tdata[d]) != 'undefined'){
60853                                         Roo.each(_this.tdata[d], function(r){
60854                                             var is_sub = '';
60855                                             var deactive = '';
60856                                             var id = r.id;
60857                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60858                                             if(r.parent_id*1>0){
60859                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60860                                                 id = r.parent_id;
60861                                             }
60862                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60863                                                 deactive = 'de-act-link';
60864                                             }
60865                                             
60866                                             row['weekday'+w] += String.format(
60867                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60868                                                     id, //0
60869                                                     r.product_id_name, //1
60870                                                     r.when_dt.format('h:ia'), //2
60871                                                     is_sub, //3
60872                                                     deactive, //4
60873                                                     desc // 5
60874                                             );
60875                                         });
60876                                     }
60877                                     d++;
60878                                 }
60879                                 
60880                                 // only do this if something added..
60881                                 if(added > 0){ 
60882                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60883                                 }
60884                                 
60885                                 
60886                                 // push it twice. (second one with an hour..
60887                                 
60888                             }
60889                             //Roo.log(result);
60890                             this.fireEvent("load", this, o, o.request.arg);
60891                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60892                         },
60893                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60894                     proxy : {
60895                         xtype: 'HttpProxy',
60896                         xns: Roo.data,
60897                         method : 'GET',
60898                         url : baseURL + '/Roo/Shop_course.php'
60899                     },
60900                     reader : {
60901                         xtype: 'JsonReader',
60902                         xns: Roo.data,
60903                         id : 'id',
60904                         fields : [
60905                             {
60906                                 'name': 'id',
60907                                 'type': 'int'
60908                             },
60909                             {
60910                                 'name': 'when_dt',
60911                                 'type': 'string'
60912                             },
60913                             {
60914                                 'name': 'end_dt',
60915                                 'type': 'string'
60916                             },
60917                             {
60918                                 'name': 'parent_id',
60919                                 'type': 'int'
60920                             },
60921                             {
60922                                 'name': 'product_id',
60923                                 'type': 'int'
60924                             },
60925                             {
60926                                 'name': 'productitem_id',
60927                                 'type': 'int'
60928                             },
60929                             {
60930                                 'name': 'guid',
60931                                 'type': 'int'
60932                             }
60933                         ]
60934                     }
60935                 },
60936                 toolbar : {
60937                     xtype: 'Toolbar',
60938                     xns: Roo,
60939                     items : [
60940                         {
60941                             xtype: 'Button',
60942                             xns: Roo.Toolbar,
60943                             listeners : {
60944                                 click : function (_self, e)
60945                                 {
60946                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60947                                     sd.setMonth(sd.getMonth()-1);
60948                                     _this.monthField.setValue(sd.format('Y-m-d'));
60949                                     _this.grid.ds.load({});
60950                                 }
60951                             },
60952                             text : "Back"
60953                         },
60954                         {
60955                             xtype: 'Separator',
60956                             xns: Roo.Toolbar
60957                         },
60958                         {
60959                             xtype: 'MonthField',
60960                             xns: Roo.form,
60961                             listeners : {
60962                                 render : function (_self)
60963                                 {
60964                                     _this.monthField = _self;
60965                                    // _this.monthField.set  today
60966                                 },
60967                                 select : function (combo, date)
60968                                 {
60969                                     _this.grid.ds.load({});
60970                                 }
60971                             },
60972                             value : (function() { return new Date(); })()
60973                         },
60974                         {
60975                             xtype: 'Separator',
60976                             xns: Roo.Toolbar
60977                         },
60978                         {
60979                             xtype: 'TextItem',
60980                             xns: Roo.Toolbar,
60981                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60982                         },
60983                         {
60984                             xtype: 'Fill',
60985                             xns: Roo.Toolbar
60986                         },
60987                         {
60988                             xtype: 'Button',
60989                             xns: Roo.Toolbar,
60990                             listeners : {
60991                                 click : function (_self, e)
60992                                 {
60993                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60994                                     sd.setMonth(sd.getMonth()+1);
60995                                     _this.monthField.setValue(sd.format('Y-m-d'));
60996                                     _this.grid.ds.load({});
60997                                 }
60998                             },
60999                             text : "Next"
61000                         }
61001                     ]
61002                 },
61003                  
61004             }
61005         };
61006         
61007         *//*
61008  * Based on:
61009  * Ext JS Library 1.1.1
61010  * Copyright(c) 2006-2007, Ext JS, LLC.
61011  *
61012  * Originally Released Under LGPL - original licence link has changed is not relivant.
61013  *
61014  * Fork - LGPL
61015  * <script type="text/javascript">
61016  */
61017  
61018 /**
61019  * @class Roo.LoadMask
61020  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61021  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61022  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61023  * element's UpdateManager load indicator and will be destroyed after the initial load.
61024  * @constructor
61025  * Create a new LoadMask
61026  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61027  * @param {Object} config The config object
61028  */
61029 Roo.LoadMask = function(el, config){
61030     this.el = Roo.get(el);
61031     Roo.apply(this, config);
61032     if(this.store){
61033         this.store.on('beforeload', this.onBeforeLoad, this);
61034         this.store.on('load', this.onLoad, this);
61035         this.store.on('loadexception', this.onLoadException, this);
61036         this.removeMask = false;
61037     }else{
61038         var um = this.el.getUpdateManager();
61039         um.showLoadIndicator = false; // disable the default indicator
61040         um.on('beforeupdate', this.onBeforeLoad, this);
61041         um.on('update', this.onLoad, this);
61042         um.on('failure', this.onLoad, this);
61043         this.removeMask = true;
61044     }
61045 };
61046
61047 Roo.LoadMask.prototype = {
61048     /**
61049      * @cfg {Boolean} removeMask
61050      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61051      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61052      */
61053     /**
61054      * @cfg {String} msg
61055      * The text to display in a centered loading message box (defaults to 'Loading...')
61056      */
61057     msg : 'Loading...',
61058     /**
61059      * @cfg {String} msgCls
61060      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61061      */
61062     msgCls : 'x-mask-loading',
61063
61064     /**
61065      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61066      * @type Boolean
61067      */
61068     disabled: false,
61069
61070     /**
61071      * Disables the mask to prevent it from being displayed
61072      */
61073     disable : function(){
61074        this.disabled = true;
61075     },
61076
61077     /**
61078      * Enables the mask so that it can be displayed
61079      */
61080     enable : function(){
61081         this.disabled = false;
61082     },
61083     
61084     onLoadException : function()
61085     {
61086         Roo.log(arguments);
61087         
61088         if (typeof(arguments[3]) != 'undefined') {
61089             Roo.MessageBox.alert("Error loading",arguments[3]);
61090         } 
61091         /*
61092         try {
61093             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61094                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61095             }   
61096         } catch(e) {
61097             
61098         }
61099         */
61100     
61101         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61102     },
61103     // private
61104     onLoad : function()
61105     {
61106         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61107     },
61108
61109     // private
61110     onBeforeLoad : function(){
61111         if(!this.disabled){
61112             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61113         }
61114     },
61115
61116     // private
61117     destroy : function(){
61118         if(this.store){
61119             this.store.un('beforeload', this.onBeforeLoad, this);
61120             this.store.un('load', this.onLoad, this);
61121             this.store.un('loadexception', this.onLoadException, this);
61122         }else{
61123             var um = this.el.getUpdateManager();
61124             um.un('beforeupdate', this.onBeforeLoad, this);
61125             um.un('update', this.onLoad, this);
61126             um.un('failure', this.onLoad, this);
61127         }
61128     }
61129 };/*
61130  * Based on:
61131  * Ext JS Library 1.1.1
61132  * Copyright(c) 2006-2007, Ext JS, LLC.
61133  *
61134  * Originally Released Under LGPL - original licence link has changed is not relivant.
61135  *
61136  * Fork - LGPL
61137  * <script type="text/javascript">
61138  */
61139
61140
61141 /**
61142  * @class Roo.XTemplate
61143  * @extends Roo.Template
61144  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61145 <pre><code>
61146 var t = new Roo.XTemplate(
61147         '&lt;select name="{name}"&gt;',
61148                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61149         '&lt;/select&gt;'
61150 );
61151  
61152 // then append, applying the master template values
61153  </code></pre>
61154  *
61155  * Supported features:
61156  *
61157  *  Tags:
61158
61159 <pre><code>
61160       {a_variable} - output encoded.
61161       {a_variable.format:("Y-m-d")} - call a method on the variable
61162       {a_variable:raw} - unencoded output
61163       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61164       {a_variable:this.method_on_template(...)} - call a method on the template object.
61165  
61166 </code></pre>
61167  *  The tpl tag:
61168 <pre><code>
61169         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61170         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61171         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61172         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61173   
61174         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61175         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61176 </code></pre>
61177  *      
61178  */
61179 Roo.XTemplate = function()
61180 {
61181     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61182     if (this.html) {
61183         this.compile();
61184     }
61185 };
61186
61187
61188 Roo.extend(Roo.XTemplate, Roo.Template, {
61189
61190     /**
61191      * The various sub templates
61192      */
61193     tpls : false,
61194     /**
61195      *
61196      * basic tag replacing syntax
61197      * WORD:WORD()
61198      *
61199      * // you can fake an object call by doing this
61200      *  x.t:(test,tesT) 
61201      * 
61202      */
61203     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61204
61205     /**
61206      * compile the template
61207      *
61208      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61209      *
61210      */
61211     compile: function()
61212     {
61213         var s = this.html;
61214      
61215         s = ['<tpl>', s, '</tpl>'].join('');
61216     
61217         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61218             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61219             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61220             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61221             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61222             m,
61223             id     = 0,
61224             tpls   = [];
61225     
61226         while(true == !!(m = s.match(re))){
61227             var forMatch   = m[0].match(nameRe),
61228                 ifMatch   = m[0].match(ifRe),
61229                 execMatch   = m[0].match(execRe),
61230                 namedMatch   = m[0].match(namedRe),
61231                 
61232                 exp  = null, 
61233                 fn   = null,
61234                 exec = null,
61235                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61236                 
61237             if (ifMatch) {
61238                 // if - puts fn into test..
61239                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61240                 if(exp){
61241                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61242                 }
61243             }
61244             
61245             if (execMatch) {
61246                 // exec - calls a function... returns empty if true is  returned.
61247                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61248                 if(exp){
61249                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61250                 }
61251             }
61252             
61253             
61254             if (name) {
61255                 // for = 
61256                 switch(name){
61257                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61258                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61259                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61260                 }
61261             }
61262             var uid = namedMatch ? namedMatch[1] : id;
61263             
61264             
61265             tpls.push({
61266                 id:     namedMatch ? namedMatch[1] : id,
61267                 target: name,
61268                 exec:   exec,
61269                 test:   fn,
61270                 body:   m[1] || ''
61271             });
61272             if (namedMatch) {
61273                 s = s.replace(m[0], '');
61274             } else { 
61275                 s = s.replace(m[0], '{xtpl'+ id + '}');
61276             }
61277             ++id;
61278         }
61279         this.tpls = [];
61280         for(var i = tpls.length-1; i >= 0; --i){
61281             this.compileTpl(tpls[i]);
61282             this.tpls[tpls[i].id] = tpls[i];
61283         }
61284         this.master = tpls[tpls.length-1];
61285         return this;
61286     },
61287     /**
61288      * same as applyTemplate, except it's done to one of the subTemplates
61289      * when using named templates, you can do:
61290      *
61291      * var str = pl.applySubTemplate('your-name', values);
61292      *
61293      * 
61294      * @param {Number} id of the template
61295      * @param {Object} values to apply to template
61296      * @param {Object} parent (normaly the instance of this object)
61297      */
61298     applySubTemplate : function(id, values, parent)
61299     {
61300         
61301         
61302         var t = this.tpls[id];
61303         
61304         
61305         try { 
61306             if(t.test && !t.test.call(this, values, parent)){
61307                 return '';
61308             }
61309         } catch(e) {
61310             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61311             Roo.log(e.toString());
61312             Roo.log(t.test);
61313             return ''
61314         }
61315         try { 
61316             
61317             if(t.exec && t.exec.call(this, values, parent)){
61318                 return '';
61319             }
61320         } catch(e) {
61321             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61322             Roo.log(e.toString());
61323             Roo.log(t.exec);
61324             return ''
61325         }
61326         try {
61327             var vs = t.target ? t.target.call(this, values, parent) : values;
61328             parent = t.target ? values : parent;
61329             if(t.target && vs instanceof Array){
61330                 var buf = [];
61331                 for(var i = 0, len = vs.length; i < len; i++){
61332                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61333                 }
61334                 return buf.join('');
61335             }
61336             return t.compiled.call(this, vs, parent);
61337         } catch (e) {
61338             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61339             Roo.log(e.toString());
61340             Roo.log(t.compiled);
61341             return '';
61342         }
61343     },
61344
61345     compileTpl : function(tpl)
61346     {
61347         var fm = Roo.util.Format;
61348         var useF = this.disableFormats !== true;
61349         var sep = Roo.isGecko ? "+" : ",";
61350         var undef = function(str) {
61351             Roo.log("Property not found :"  + str);
61352             return '';
61353         };
61354         
61355         var fn = function(m, name, format, args)
61356         {
61357             //Roo.log(arguments);
61358             args = args ? args.replace(/\\'/g,"'") : args;
61359             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61360             if (typeof(format) == 'undefined') {
61361                 format= 'htmlEncode';
61362             }
61363             if (format == 'raw' ) {
61364                 format = false;
61365             }
61366             
61367             if(name.substr(0, 4) == 'xtpl'){
61368                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61369             }
61370             
61371             // build an array of options to determine if value is undefined..
61372             
61373             // basically get 'xxxx.yyyy' then do
61374             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61375             //    (function () { Roo.log("Property not found"); return ''; })() :
61376             //    ......
61377             
61378             var udef_ar = [];
61379             var lookfor = '';
61380             Roo.each(name.split('.'), function(st) {
61381                 lookfor += (lookfor.length ? '.': '') + st;
61382                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61383             });
61384             
61385             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61386             
61387             
61388             if(format && useF){
61389                 
61390                 args = args ? ',' + args : "";
61391                  
61392                 if(format.substr(0, 5) != "this."){
61393                     format = "fm." + format + '(';
61394                 }else{
61395                     format = 'this.call("'+ format.substr(5) + '", ';
61396                     args = ", values";
61397                 }
61398                 
61399                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61400             }
61401              
61402             if (args.length) {
61403                 // called with xxyx.yuu:(test,test)
61404                 // change to ()
61405                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61406             }
61407             // raw.. - :raw modifier..
61408             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61409             
61410         };
61411         var body;
61412         // branched to use + in gecko and [].join() in others
61413         if(Roo.isGecko){
61414             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61415                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61416                     "';};};";
61417         }else{
61418             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61419             body.push(tpl.body.replace(/(\r\n|\n)/g,
61420                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61421             body.push("'].join('');};};");
61422             body = body.join('');
61423         }
61424         
61425         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61426        
61427         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61428         eval(body);
61429         
61430         return this;
61431     },
61432
61433     applyTemplate : function(values){
61434         return this.master.compiled.call(this, values, {});
61435         //var s = this.subs;
61436     },
61437
61438     apply : function(){
61439         return this.applyTemplate.apply(this, arguments);
61440     }
61441
61442  });
61443
61444 Roo.XTemplate.from = function(el){
61445     el = Roo.getDom(el);
61446     return new Roo.XTemplate(el.value || el.innerHTML);
61447 };